Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enforce calling order of destructors

I am trying to get the following setup right:

A given application (with multiple source files, compilation units) has global variables of type class A defined in many compilation units. These should be "managed" by a new to introduce class B (where only 1 instance should exist) in the sense that upon creation they "register" themselves at instance of class B and at destruction "sign off".

Setting the thing up for the constructors to work is fairly straight-forward. One can use:

types.h:

class B {
  static B& Instance() {
    static B singleton;
    return singleton;
  }
  void registerA( const A& a ) { 
  // whatever
  }
};


class A {
  A() { B::Instance().registerA( this ); }
};

How to get the destructors right? If using:

class A {
  A() { B::Instance().registerA( this ); }
  ~A() { B::Instance().signoffA( this ); }
};

then the destructor of B might be called before the destructor of A. Then the instance of class A signs off at a just newly created instance of B.

The test case would be a multi source file setup with definitions of instances of class A in a namespace:

file1.cc

#include "types.h"
namespace C {
   A a;
}

file2.cc

#include "types.h"
namespace C {
   A b;
}

I guess on can do such thing easily with Boost smart pointers. However, if possible I would like to avoid using additional libraries to keep dependence as low as possible.

One thing that might help: All global variables are in a named namespace.

like image 757
ritter Avatar asked May 19 '12 17:05

ritter


1 Answers

I think you're fine. Here's 3.6.3 on "Termination":

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.

Suppose you have the following setup:

struct A;

struct B
{
    static B & get() { static B impl; return impl; }
    void registrate(A *);

private:
    B() { /* complex stuff */ }
    // ...
};

struct A { A() { B::get().registrate(this); } };

A a1;

Now whatever happens, the first constructor of a static A-type object will call B::get(), which sequences the construction of the static impl object before the completion of the first A-constructor. By the above clause, this guarantees that the destructor of the B impl-object is sequenced after all the A-destructors.

like image 96
Kerrek SB Avatar answered Sep 18 '22 09:09

Kerrek SB