我正在探索与单身人士相关的Boost.Serialization中一个非常棘手的错误。对于上下文:Boost 1.65更改了单例的实现,打破了is_destructed
通知,导致程序退出或库卸载时出现段错误。提升1.66“固定”,但泄漏记忆。
单例代码(与此问题相关)归结为:
template<class T> struct singleton{
T& inst(){
static T t;
return t;
}
}
使用静态成员函数变量可以避免static init fiasco,但仍然存在与销毁相同的问题。
然而,Finding C++ static initialization order problems显示了代码如何解决这个问题:当A
的Ctor使用B
时,B
将首先被构造,因此最后被解构。这也在Destruction order of static objects in C++中说明。 (completion of the destructor happens in the reverse order of the completion of the constructor
)
到目前为止一切顺利。 Boost.Serialization现在使用多个extended_type_info_typeid<T>
类型的单例来在另一个单例T
中注册usertype std::multiset<const bs::typeid_system::extended_type_info_typeid_0*,...>
的一些元数据。这是通过使用multiset
的构造函数中的extended_type_info_typeid_0
(假设来自此处的所有单例)来完成的。在extended_type_info_typeid_0
的析构函数中,multiset
中的条目已被删除。
这意味着我们完全符合上述情况,multiset
应该比其他实例更长。
使用共享库时会出现故障。我有以下测试用例:
test_multi_singleton.cpp:
int f();
int g();
int main(int argc, char**){
// Make sure symbols are used
if(argc==8) return f();
if(argc==9) return g();
}
multi_singleton1.cpp:
#include <boost/serialization/extended_type_info_typeid.hpp>
int f(){
return 0 != boost::serialization::extended_type_info_typeid<int>::get_const_instance().get_key();
}
multi_singleton2.cpp:
#include <boost/serialization/extended_type_info_typeid.hpp>
int g(){
// Use different(!) type
return 0 != boost::serialization::extended_type_info_typeid<float>::get_const_instance().get_key();
}
Build with:
g++ multi_singleton1.cpp -lboost_serialization -fPIC -shared -olibmulti_singleton1.so
g++ multi_singleton2.cpp -lboost_serialization -fPIC -shared -olibmulti_singleton2.so
g++ test_multi_singleton.cpp -L. -lmulti_singleton1 -lmulti_singleton2
Run in valgrind:
valgrind ./a.out
可以看出,这会破坏Boost 1.65中的记忆。原因是我通过劫持和记录ctor / dtor调用来追踪乱码:
ctor 0x7f9f0aa074a0 std::multiset<const extended_type_info_typeid_0*>
ctor 0x7f9f0a7f63e0 extended_type_info_typeid<float>
ctor 0x7f9f0a7f64a0 std::multiset<const extended_type_info_typeid_0*>
ctor 0x7f9f0aa073e0 extended_type_info_typeid<int>
dtor 0x7f9f0aa073e0 extended_type_info_typeid<int>
dtor 0x7f9f0aa074a0 std::multiset<const extended_type_info_typeid_0*>
dtor 0x7f9f0a7f64a0 std::multiset<const extended_type_info_typeid_0*>
dtor 0x7f9f0a7f63e0 extended_type_info_typeid<float>
这是使用GCC 6.4,但与GCC 7.1相同。正如您所看到的那样,2个多重集合在第二个extended_type_info_typeid
之前被一起销毁。
我错过了什么吗?这是否允许C ++标准?
答案 0 :(得分:1)
如果构造函数完成或动态初始化了 具有静态存储持续时间的对象强烈地发生在之前 另外,对第二个析构函数的完成进行了排序 在第一个析构函数启动之前。
此外:
对于数组或类类型的对象,该对象的所有子对象都是 在具有静态存储持续时间的任何块范围对象之前销毁之前的 在子对象建设期间初始化被破坏。