Как выполнить статическую деинициализацию, если деструктор имеет побочные эффекты и доступ к объекту осуществляется из деструктора другого статического объекта?
Существует простой и хорошо известный шаблон, позволяющий избежать статического сбоя при инициализации, описанный в разделе 10.13 C++ FAQ Lite.
В этом стандартном шаблоне есть компромисс, заключающийся в том, что либо построенный объект никогда не будет разрушен (что не является проблемой, если деструктор не имеет важных побочных эффектов), либо к статическому объекту нельзя безопасно получить доступ из деструктора другого статического объекта (см. раздел 10.14 C++ FAQ Lite).
Поэтому мой вопрос: как избежать фиаско деинициализации статической информации, если деструктор статического объекта имеет важные побочные эффекты, которые должны в конечном итоге возникнуть, и к статическому объекту должен получить доступ другой деструктор статического объекта?
(Примечание: в FAQ-lite упоминается, что на этот вопрос дан ответ в FAQ 16.17 FAQ по C++: часто задаваемые вопросы М. Клайна и Г. Ломова. У меня нет доступа к этой книге, поэтому я задаю этот вопрос вместо этого.)
3 ответа
Функциональные статические объекты, такие как глобальные объекты, гарантированно уничтожаются (при условии, что они созданы).
Порядок разрушения обратен творению.
Таким образом, если объект зависит от другого объекта во время уничтожения, вы должны гарантировать, что он все еще доступен. Это относительно просто, так как вы можете навести порядок уничтожения, убедившись, что порядок создания выполнен правильно.
Следующая ссылка о синглтонах, но описывает похожую ситуацию и ее решение:
Нахождение C++ статических проблем порядка инициализации
Экстраполируя на общий случай ленивых инициализированных глобалов, как описано в разделе FAQ, мы можем решить проблему следующим образом:
namespace B
{
class B { ... };
B& getInstance_Bglob;
{
static B instance_Bglob;
return instance_Bglob;;
}
B::~B()
{
A::getInstance_abc().doSomthing();
// The object abc is accessed from the destructor.
// Potential problem.
// You must guarantee that abc is destroyed after this object.
// To gurantee this you must make sure it is constructed first.
// To do this just access the object from the constructor.
}
B::B()
{
A::getInstance_abc();
// abc is now fully constructed.
// This means it was constructed before this object.
// This means it will be destroyed after this object.
// This means it is safe to use from the destructor.
}
}
namespace A
{
class A { ... };
A& getInstance_abc()
{
static A instance_abc;
return instance_abc;
}
}
Это что-то вроде хака, но я бы добавил несколько статических bools для отслеживания порядка деинициализации. Затем объект, который заканчивается последним, выполняет очистку, даже если он не является владельцем.
Пока статический деструктор другого объекта запускается первым, все в порядке. Вы можете убедиться в этом, построив другой объект перед "объектом A". Пока оба объекта объявлены в одном и том же модуле компиляции, они будут инициализированы в порядке их появления в исходном коде и уничтожены в обратном порядке.
Если вам нужно, чтобы это произошло в разных единицах компиляции, вам не повезло. Лучше создавать их динамически во время выполнения и уничтожать их в конце main, а не делать их статичными.