c – 共享库中静态函数成员的销毁顺序

我正在探索与单身人士相关的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++中说明.(析构函数的完成以与构造函数完成相反的顺序发生)

到现在为止还挺好. Boost.Serialization现在使用类型为extended_type_info_typeid< T>的多个单体.在另一个单独的std :: multiset< const bs :: typeid_system :: extended_type_info_typeid_0 *,…>中注册usertype T的一些元数据.这是通过使用extended_type_info_typeid_0的构造函数中的multiset(假设来自此处的所有单例)来完成的.在extended_type_info_typeid_0的析构函数中,删除了多集中的条目.

这意味着我们完全具有上述情况,并且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标准允许的吗?

最佳答案 从
basic.start.term/3开始:

If the completion of the constructor or dynamic initialization of an
object with static storage duration strongly happens before that of
another, the completion of the destructor of the second is sequenced
before the initiation of the destructor of the first.

也:

For an object of array or class type, all subobjects of that object are
destroyed before any block-scope object with static storage duration
initialized during the construction of the subobjects is destroyed.

点赞