8

To the following code:

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

I have a couple of questions:

  1. Is the order of destruction calls well defined? (even if class C and D are defined in different source-files)
  2. If it is well defined is this behaviour portable?
Paul Floyd
  • 5,530
  • 5
  • 29
  • 43
user1235183
  • 3,002
  • 1
  • 27
  • 66
  • I don't think the standard actually defines it, as it doesn't define the destruction order of global objects , only that they supposed to be destructed in the reverse order they where constructed – David Haim Oct 25 '16 at 14:02
  • Order of destruction is well-defined within a single [translation unit](https://en.wikipedia.org/wiki/Translation_unit_(programming)). But not between translation units. – Some programmer dude Oct 25 '16 at 14:02
  • [A related question and answers](http://stackoverflow.com/q/246564/440558). – Some programmer dude Oct 25 '16 at 14:28

2 Answers2

12

The point of this construct is to impose a construction order (and thus a destruction order).

Construction

Since these are local static variables, the order of construction is determined by the order in which their respective Instance functions are called for the first time.

Since that is done in main, the construction order is fully specified.

The only way to make the order unspecified is if you use them in static initialisation in different translation units, for instance if one has

C& the_c = C::Instance();

and the other has

D& the_d = D::Instance();

Destruction

The destruction of objects with static storage is the reverse of the order of construction.

3.6.3, Termination, paragraph 1:

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

So the destruction order is fully specified by the construction order.

Note that this singleton construct is well specified even if one of them depends on the other, regardless of translation unit.

That is, this is perfectly safe, and it doesn't matter where it's defined:

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;
};
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
-1

In general, the order of destruction of static objects including static local variables (that are used in your case) is undefined.

To ensure order of destruction, one pattern I used is to leverage on std::shared_ptr. For instance, if you want to ensure the C singleton remains valid until after the D singleton is destroyed, you could store a shared_ptr< C > in 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_;
};
Donghui Zhang
  • 1,133
  • 6
  • 8
  • [molbdnilo's answer](http://stackoverflow.com/a/40243538/4115625) explained the order of destruction, -1 – Danh Oct 25 '16 at 15:48