迈耶斯单身人士的破坏秩序

时间:2016-10-25 13:59:53

标签: c++ language-lawyer

以下代码:

class C {
public:    
    static C& Instance() {
       static C c;
       return c;
    }

   ~C(){std::cout << "c destructed\n";}
private:
   C(){}
};

class D{//similar to C but prints `d destructed` on destruction
//...

int main()
{
    auto c = C::Instance();
    auto d = D::Instance();
}
//outputs (with gcc)
//d destructed
//c destructed
//d destructed
//c destructed

我有几个问题:

  1. 破坏的顺序是否定义明确? (即使C类和D类在不同的源文件中定义)
  2. 如果定义得好,这种行为是否可移植?

2 个答案:

答案 0 :(得分:8)

这个结构的要点是施加施工顺序(因此也就是破坏顺序)。

<强>建筑

由于这些是局部静态变量,因此构造顺序取决于第一次调用各自Instance函数的顺序。

由于这是在main中完成的,因此完全指定了构造顺序。

使订单未指定的唯一方法是,如果您在不同翻译单元中的静态初始化中使用它们,例如,如果有

C& the_c = C::Instance();

而另一个

D& the_d = D::Instance();

<强>销毁

使用静态存储破坏对象与构造顺序相反。

3.6.3,终止,第1段:

  

如果构造函数完成或动态初始化了   具有静态存储持续时间的对象在之前被排序   另外,对第二个析构函数的完成进行了排序   在第一个析构函数启动之前。

因此,销毁订单完全由施工订单指定。

请注意,即使其中一个构造依赖于另一个构造,也可以很好地指定这个单例构造,而不管翻译单元。

也就是说,这是非常安全的,无论它在何处定义都无关紧要:

class C {
public:    
    static C& Instance() {
       static C c(D::Instance());
       return c;
    }

   ~C(){ m_d.doSomething(); } // Yes, this is safe.
private:
   C(D& d) : m_d(d) { m_d.doSomething(); } // Yes, this is safe.
   D& m_d;
};

答案 1 :(得分:-1)

通常,静态对象的破坏顺序(包括在您的情况下使用的静态局部变量)是未定义的。

为了确保销毁顺序,我使用的一种模式是利用std :: shared_ptr。例如,如果要确保C单例在D单例被销毁之前保持有效,则可以存储shared_ptr&lt; C>在D:

class C {
  static shared_ptr<C> Instance() {
    static auto c = make_shared<C>();
    return c;
  }
};

class D {
  static shared_ptr<D> Instance() {
    static auto d = make_shared<D>();
    return d;
  }

  D(): c_(C::Instance()) {}

 private:
  shared_ptr<C> c_;
};