在C ++中模仿静态构造函数

Imitate a static constructor in C++

这是一个与C++中对象初始化有关的问题。

我有一组类(不是实例),继承自一个公共的基类,当程序启动时,我需要它们在一个容器(特别是一个映射)中注册关于它们自己的信息。

问题是我需要它是动态的。容器是在独立的项目中定义的,与类不同。我希望避免生成库的多个硬编码版本,每个使用库的程序中的每一组类都有一个硬编码版本。

我考虑在每个子类中都有一个特殊类的静态实例,这样就可以在其构造函数中注册。但是,我找不到任何方法来保证容器在建造这些对象之前就已经建造好了。

我还应该注意,在创建这些子类的任何实例之前,容器中关于子类的信息应该是可用的。

有没有办法做到这一点,或者模仿C++中的静态构造函数?


这是反对OOP范式的,但是让静态成员组成一个由2个全局变量引导的链接列表怎么样?你可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ClassRegistrator *head=NULL;
ClassRegistrator *tail=NULL;

struct ClassRegistrator {
    ... //data that you need
    ClassRegistrator *next;
    ClassRegistrator(classData ...) {
      if (head==NULL) head=tail=this;
      else {
        tail->next=this;
        tail=this;
      }
      ... //do other stuff that you need for registration
    }
};


class MyClass { //the class you want to register
    static ClassRegistrator registrator;
}

ClassRegistrator MyClass::registrator(...); //call the constructor

我相信全局变量,因为它们不需要有一个构造函数,但只是纯数据,所以在开始执行代码时,它们肯定已经初始化了。

显然,这不是线程安全的,等等,但应该让您的工作完成。


你在同时描述不同的问题。对于某些静态初始化的特殊问题,一种简单的方法是创建一个将执行注册的假类。然后,每个不同的类都可以有一个static const X成员,该成员必须在翻译单元中定义,并且该定义将触发实例的实例化和类的注册。

这并不能解决困难的问题,那就是初始秩序的崩溃。语言不能保证不同翻译单元中对象的初始化顺序。也就是说,如果用这样的类编译三个翻译单元,就不能保证假成员的相对执行顺序。这也适用于库:如果要在其中注册类的容器是全局/静态成员属性,则不保证该容器已初始化。

如果您有权访问代码,可以修改容器代码以使用static local variables,这将是确保初始化顺序的一个进步。作为可能解决方案的草图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// registry lib
class registry { // basically a singleton
public:
   static registry& instance() { // ensures initialization in the first call
      static registry inst;
       return inst;
   }
// rest of the code
private:
   registry(); // disable other code from constructing elements of this type
};
// register.h
struct register {
   template <typename T>
   register( std::string name ) {
       registry::instance().register( name, T (*factory)() ); // or whatever you need to register
   }
};
// a.h
class a {
public:
   static a* factory();
private:
   static const register r;
};
// a.cpp
const register a::r("class a", a::factory );
// b.h/b.cpp similar to a.h/a.cpp

在这种情况下,ab类的注册没有明确的顺序,但这可能不是问题。另一方面,通过在registry::instance函数中使用局部静态变量,保证在调用registry::register方法(作为对instance方法的第一次调用的一部分)之前执行单例初始化。

如果您不能进行这种更改,那么您基本上是运气不好,并且您不能保证在其他翻译单元中的其他静态成员属性(或全局属性)之前实例化registry。如果是这种情况,则必须将类的注册推迟到第一个实例化,并向要注册的每个类的构造函数添加代码,以确保在实际构造对象之前注册该类。

这可能是一个解决方案,具体取决于其他代码是否创建该类型的对象。在工厂功能的特殊情况下(首先想到的),如果不允许任何其他东西创建ab类型的对象…那么,在构造函数调用上支持注册的Piggy也不是一个解决方案。


您可以使用"首次使用时初始化"模式,然后实例化一个虚拟静态实例以确保尽早初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class cExample
{
    public :
       cExample() ;

       // Static functions here

    private :
        static bool static_init ;

        // other static members here
}

cExample::static init = false ;

cExample::cExample()
{
    // Static initialisation on first use
    if( !static_init )
    {
        // initialise static members
    }

    // Instance initialisation here (if needed)
}

// Dummy instance to force initialisation before main() (if necessary)
static cExample force_init ;

请参阅:http://www. PARASIFIF.COM/C++-FAQLIT/Ctos.

一种选择是,当第一件事添加到容器中时,惰性地构造容器:

1
2
3
4
5
  void AddToContainer(...) {
    // Will be initialized the first time this function is called.
    static Container* c = new Container();
    c->Add(...);
  }

"模仿"静态构造函数的唯一方法是显式调用函数来执行静态初始化。只有在模块中链接才能运行代码pre-main。


一种方法是将注册函数传递给类。每个后代将执行要注册的函数。此函数可以在构造函数中传递。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Registration_Interface
{
  virtual void operator() (const std::string& component_name) = 0;
};

struct Base
{
};

struct Child1
  : public Base
{
  Child(Registration_Interface& registration_ftor)
  {
     //...
     registration_ftor("Child1");
  }
};

这是单例模式的候选者。基本上,当子类的第一个实例被实例化时,您希望容器被实例化。这可以通过检查基类构造函数中的单例指针是否为空来实现,如果为空,则实例化容器。