【问题标题】:How can I tell if a static object has been destroyed in C++11如何判断静态对象是否已在 C++11 中销毁
【发布时间】:2019-03-17 05:29:17
【问题描述】:

在 C++11 规范中,basic.start.term 1 声明:

如果用静态存储完成了构造函数或对象的动态初始化 持续时间在另一个之前排序,第二个析构函数的完成是排序的 在第一个析构函数启动之前。 [注意:这个定义允许并发销毁。 ——尾注]

在 C++03 中,我的析构函数是有序的。顺序可能没有指定,但它们是有序的。这对于必须注册自己的静态对象非常有用。规范中没有多线程的概念,因此规范中没有无序析构函数的概念。我所知道的实现多线程的编译器在单线程环境中进行了破坏。

a.cpp:
  struct A
  {
       A()
       : mRegistration(0)
       { }

       ~A()
       {
           if (mRegistration)
             tryUnregisterObject(mRegistration);
       }

       void registerNow()
       {
           mRegistration = registerObject(this);
       }
  };

  A myA;

b.cpp:


  class Registrar
  {
      public:
           Registrar()
           {
               isAlive = true;
           }
           ~Registrar()
           {
               isAlive = false;
           }
      ...
  };

  bool isAlive = false; // constant initialization
  static Registrar& registrar()
  {
      static Registrar instance;
      return instance;
  }

  int registerObject(void* obj)
  {
      registar().register(obj);
  }

  void tryUnregisterObject(void* obj)
  {
     if (isAlive) {
        registrar().unregister(obj);
     } else {
        // do nothing.  registrar was destroyed
     }
  }

在这个例子中,我不能保证myARegistrar 的销毁顺序,因为它们在不同的编译单元中。但是,我至少可以检测出它们发生的顺序并采取相应的行动。

在 C++11 中,这种方法会围绕 isAlive 变量创建数据竞争。这可以在构建过程中解决,因为我可以创建一个像互斥锁一样的同步对象来在我第一次需要它时保护它。但是,在销毁的情况下,我可能必须在我的互斥锁被销毁后检查 isAlive!

有没有办法在 C++11 中解决这个问题?我觉得我需要一个同步原语来解决问题,但是我尝试过的所有事情都会导致原语在保护我需要保护的东西之前被破坏。如果我要使用 Windows 或 PThreads 线程原语,我可以简单地选择不调用析构函数并让操作系统在我之后进行清理。但是,C++ 对象会自行清理。

【问题讨论】:

  • 我猜“避免静态”不是一个可以接受的答案...... :)
  • 您需要注册的对象是否可以有一个包含 shared_ptr 的附加成员...例如,取消注册单例?然后 shared_ptr 语义将控制注销对象的生命周期。那应该安全吗?
  • 另外,如果你确实需要一个同步原语,你可以用原子构建一个 - 或者可能已经有一个 - 因为它实际上并没有被破坏,所以可以安全地以任何顺序使用......

标签: c++11 destructor exit


【解决方案1】:

[basic.start.init]/2 如果程序启动线程(30.3),则变量的后续初始化相对于在不同翻译中定义的变量的初始化是无序的单元。否则,相对于在不同翻译单元中定义的变量的初始化,变量的初始化是不确定顺序。如果程序启动一个线程,则变量的后续无序初始化相对于其他所有动态初始化都是无序的。否则,变量的无序初始化相对于其他所有动态初始化是不确定顺序的

(强调我的。)因此,只要您不在任何静态对象的构造函数中启动线程,您就可以获得与标准早期版本相同的保证。

【讨论】:

  • 反例:在并发销毁的可能性之前意味着如果您有 2 个静态对象以某种方式共享一个资源,以前您可以使用一个简单的计数器来决定哪个静态析构函数应该销毁共享的对象,即使排序是不确定的。对于并发销毁,该计数器至少需要是原子的。
  • @GemTaylor 如果没有并发构造,也不应该有并发销毁,根据问题中引用的 [basic.start.term]/1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-19
  • 2015-01-13
相关资源
最近更新 更多