Singletony w statycznej bibliotece i kolejność destrukcji

0

Cześć,
w ramach pisania biblioteki, która ma być zbiorem różnych pomocnych narzędzi (docelowo piszę to jako API i jako silnik do gier), postanowiłem napisać własny menedżer pamięci. Postanowiłem także podmienić std::allocator w kontenerach STL na swój własny, np.

namespace GLOWE
{
	template<class T>
	class Vector final : public std::vector<T, GLOWE::template StdAllocator<T>>
	{
	public:
		virtual ~Vector(void) = default;
	};
}

Aby swój menedżer pamięci udostępnić metodom i funkcjom wewnątrz API oraz użytkownikowi, stworzyłem prostą klasę template OneInstance (według wzorca Singleton):

namespace GLOWE
{
	template<class T>
	class OneInstance final
	{
	private:
		static std::unique_ptr<T> data;
	public:
		OneInstance(void) = delete;
		OneInstance(_In_ const OneInstance<T>& arg) = delete;
		OneInstance(_In_ OneInstance<T>&& arg) = delete;
		~OneInstance(void) = delete;

		static T& instance(void);
	};

	template<class T>
	std::unique_ptr<T> OneInstance<T>::data;

	template<class T>
	T & OneInstance<T>::instance(void)
	{
		static_assert(std::is_class<T>::value, "OneInstance works only for classes.");

		if (!OneInstance<T>::data)
		{
			OneInstance<T>::data = std::make_unique<T>();
		}

		return *OneInstance<T>::data;
	}

	template<class T>
	inline T& getInstance(void)
	{
		return OneInstance<T>::instance();
	}
}

Niestety, kolejność destrukcji jest nieznana/niezdefiniowana. Tworzy to problem, gdy podczas wywoływania konstruktorów np. menedżer pamięci został już usunięty, ale taki Vector lub String w innym Singletonie, który jest, niestety, niszczony potem, wymaga istnienia tego menedżera, aby zdealokować swoją pamięć. Powstaje błąd i program się wywala.
Czy mogę jakoś to naprawić? Każda pomoc będzie mile widziana.

0

Jeśli to robisz w celu innym niż edukacyjny, to może się okazać, że się napracujesz, a uzyskasz gorszy produkt.

Pomijając już dyskusję o antypatternach, sam singleton można napisać dużo lepiej (np. zapewnić thread-safe inicjalizację), polecam ten art. by @satirev

Co do kolejności inicjalizacji (a zatem i destrukcji) - nie ma w C++ standardowej metody ustalania ich pomiędzy różnymi TU, więc jesteś zdany na pragmy kompilatora, którego używasz.

0

A po co stworzyłeś klasę dziedziczącą po std::vector? Zdajesz sobie sprawę, że destruktor w klasie std::vector nie jest wirtualny?
Jeśli chcesz mieć jakąś kontrolę nad inicjalizacją staticów, to opakuj je w funkcje. Wówczas są one inicjalizowane w momencie pierwszego wywołania takiej funkcji.
Ja natomiast zastanowiłbym się, czy w ogóle potrzebujesz singletona. W 99% przypadków jest to antywzorzec, którego powinno się unikać.

1

@MarekR22:
A to, to co jest?

class Vector final : public std::vector<T, GLOWE::template StdAllocator<T>>

Poza tym to się chyba nie tak robi, prawda? Są bardziej koszerne sposoby, jak choćby:

template<class T>
using Vector = std::vector<T, GLOWE::template StdAllocator<T>>;

//i użycie
Vector<int> v;

1 użytkowników online, w tym zalogowanych: 0, gości: 1