Code: Select all
Type& GetObject()
{
static Type g_Object;
return g_Object;
}
Code: Select all
int main()
{
...
GetObject1();
GetObject2();
...
}
Wenn das Programm in einer gehosteten Umgebung normal beendet werden würde, würde die Zerstörung in der umgekehrten Reihenfolge der Initialisierung erfolgen, was in Ordnung ist. Da das Programm in diesem Fall jedoch nicht normal beendet wird, trifft dies nicht zu. Vielleicht kann std::exit verwendet werden (habe ich noch nicht versucht), aber es führt auch zusätzliche Vorgänge aus, die nicht benötigt werden (z. B. das Leeren von C-Streams).
Potenzielle Lösung
Diese Lösung beinhaltet einen Wrapper-Union-Typ, der darauf angewiesen ist, dass der Benutzer eine explizite Initialisierung und Zerstörung durchführt. Der Konstruktor initialisiert den trivialen Typ-Member, wodurch er sicher in jeder globalen Reihenfolge initialisiert werden kann, und der Destruktor tut nichts.
Unter der Haube verwendet er die Platzierung „new“ zum Initialisieren und einen Aufruf des Destruktors des zugrunde liegenden Typs, wenn er zerstört wird.
Es liegt am Benutzer, den Wrapper korrekt zu verwenden – andernfalls undefiniertes Verhalten.
Code: Select all
#include
#include
template
union Lazy_t
{
public:
inline Lazy_t() : m_uninitialized{} {}
inline ~Lazy_t() {}
template
inline void Initialize(Args&&... args) { ::new (&m_initialized) T(std::forward(args)...); }
inline void Destroy() { m_initialized.~T(); }
inline T& operator* () { return m_initialized; }
inline const T& operator* () const { return m_initialized; }
inline T* operator->() { return &m_initialized; }
inline const T* operator->() const { return &m_initialized; }
private:
struct Uninitialized_t {};
Uninitialized_t m_uninitialized;
T m_initialized;
};
Code: Select all
struct NonTrivial_t
{
NonTrivial_t() : m_value{} { m_value = new int{}; }
~NonTrivial_t() { delete m_value; }
int* m_value;
};
Lazy_t g_Object1;
Lazy_t g_Object2;
void Main()
{
// ... microcontroller initialization ...
g_Object1.Initialize();
g_Object2.Initialize();
// ...
*g_Object1->m_value = 1;
*g_Object2->m_value = 2;
// ...
g_Object2.Destroy();
g_Object1.Destroy();
// ... microcontroller de-initialization and enter shutdown ...
}
Anhang: Lazy Type (Wrapper) mit Sicherheitsprüfungen:
Code: Select all
#include
template
class SafeLazy_t
{
public:
inline SafeLazy_t()
: m_lazy{}
#ifndef NDEBUG
, m_initialized{}
#endif
{}
inline ~SafeLazy_t()
{
#ifndef NDEBUG
assert(!m_initialized);
#endif
}
template
inline void Initialize(Args&&... args)
{
#ifndef NDEBUG
assert(!m_initialized);
#endif
m_lazy.Initialize(std::forward(args)...);
#ifndef NDEBUG
m_initialized = true;
#endif
}
inline void Destroy()
{
#ifndef NDEBUG
assert(m_initialized);
#endif
m_lazy.Destroy();
#ifndef NDEBUG
m_initialized = false;
#endif
}
inline T& operator*()
{
#ifndef NDEBUG
assert(m_initialized);
#endif
return *m_lazy;
}
inline const T& operator*() const
{
#ifndef NDEBUG
assert(m_initialized);
#endif
return *m_lazy;
}
inline InnerLazy_t& operator->()
{
#ifndef NDEBUG
assert(m_initialized);
#endif
return m_lazy;
}
inline const InnerLazy_t& operator->() const
{
#ifndef NDEBUG
assert(m_initialized);
#endif
return m_lazy;
}
private:
Lazy_t m_lazy;
#ifndef NDEBUG
bool m_initialized;
#endif
};
Mobile version