【问题标题】:Classes with static members containing static members in C++具有静态成员的类包含 C++ 中的静态成员
【发布时间】:2012-11-24 16:51:48
【问题描述】:

我有一个包含静态成员“obj”的类。静态类成员 obj 本身包含一个静态成员(恰好是互斥类型的类)。

现在,当我的程序终止时,它会崩溃。当静态对象“obj”被破坏时,就会发生这种情况。 'obj' 的析构函数调用它的静态成员(我自己的互斥锁类型遵循 RAII 习语来破坏底层的低级对象)。不幸的是,这个成员碰巧已经被销毁了,因为静态对象的初始化顺序(->相反的析构函数顺序)是未定义的。

如何干净地生存下来?我很惊讶这种情况并没有更频繁地发生。一般来说,拥有静态的非 POD 成员似乎非常危险。尤其是如果您不太了解它们的内部结构。

【问题讨论】:

  • 说真的,只是重新设计没有任何静态...除了拥有一个全局互斥锁是一个非常糟糕的想法。
  • 一些源代码 sn-ps 会有所帮助。
  • @ybungalobill 如果不使用另一个互斥锁,您如何保护互斥锁的创建?在这个链中的某个时刻,您可能需要一个全局互斥锁。 (我知道,在开始线程之前创建所有需要的互斥锁会更干净。但在某些情况下,这违背了封装。)
  • 互斥体被封装在类中,用于使类线程安全。为了保护所有实例,它必须是 static ='global'。这些实例共享一些共同的项目。谢谢大家的cmets!我想作为第一个治疗方法,我使包含互斥锁的类成员成为非静态的,并在这种情况下支付少量罚款以生成更多实例。然后我检查应该移动到单例。

标签: c++ static destruction


【解决方案1】:

如果您将它们设为static 在函数内部,那么您将根据谁首先调用该函数来对它们的生命周期进行排序。这就是单例的实现方式。

foo& get_foo() {
  static foo instance;
  return instance;
}

bar& get_bar() {
  static bar instance;
  return instance;
}

不过,你最好避免静态。

【讨论】:

    【解决方案2】:

    这是避免动态分配时的常见问题。

    我避免这个问题的方法是遵循“静态类”的模式——通常静态数据属于没有静态数据的实例的“管理器”类。然后在显式“初始化”调用中处理静态数据初始化,并在仅具有静态数据和静态成员函数的管理器类上显式“关闭”调用处理破坏。

    您可能必须使用 new 或 delete 并明确说明您在做什么 - 从这个意义上说,您否定了自动机制为您工作的优势,但作为交换,您可以获得可靠且易于调试的初始化和关闭例程.

    这种方法本质上创建了一个单例,因此没有管理器类的实例 - 基本上是静态数据的负载和一些具有类语法的 C 函数,提供封装的好处(例如,私有成员不能在类外部访问)

    这在许多情况下对编译器也很友好,因为它知道所有数据在哪里以及如何处理它

    【讨论】:

      【解决方案3】:

      如果您需要静态对象使用的对象,请在 构造函数或析构函数,通常最好使用 单例模式的变化。这解决了顺序 初始化问题,如果你做对了,对象会 永远不会被破坏,所以不应该有破坏的顺序 也有问题。

      因为您的应用程序显然是多线程的(因为您有 互斥体),您应该采取通常的预防措施来使 对象线程安全,通过确保在进入之前进行初始化 主要的。基本思想是这样的:

      template <typename T, char const* id>
      class StaticInstanceWrapper
      {
          static T* myObject;
      public:
          static T& instance();
      };
      
      template <typename T, char const* id>
      T* StaticInstanceWrapper<T, char const* id>::myObject =
              &StaticInstanceWrapper<T>::instance();
      
      template <typename T, char const* id>
      T& StaticInstanceWrapper<T>::instance()
      {
          if ( myObject == NULL ) {
              myObject = new T;
          }
          return *myObject;
      }
      

      然后您将静态对象定义为static StaticInstanceWrapper<Whatever> obj;,并将其访问为 obj.instance().someFunction(),而不仅仅是 obj.someFunction()(不会编译,因为 obj 不会 有一个someFunction() 成员)。

      请注意,StaticInstanceWrapper 的每个实例都有一个 不同的类型,所以有唯一的静态成员,你必须 强制模板的单独实例化。这就是为什么我们 hae id 模板参数;这个论点的类型可以 实际上是任何东西,只要每个实例都有一个 唯一标识符。在实践中,我可能会使用宏 定义,大致如下:

      #define DEFINE_STATIC_INSTANCE_WRAPPER(type, name) \
      char const PASTE(name, _Identifier)[] = STRINGIZE(name); \
      StaticInstanceWrapper<type, PASTE(name, _Identifier)> name
      

      这确保了每个实例都有一个唯一的 ID。 (如果你想 为了变得更漂亮,你也可以加入__LINENO__,但是 因为无论如何名称必须在范围内是唯一的,我怀疑 这是必要的。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-05
        • 2013-05-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多