【问题标题】:Imitate a static constructor in C++在 C++ 中模仿静态构造函数
【发布时间】:2011-07-16 00:59:42
【问题描述】:

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

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

问题是我需要它是动态的。容器是在一个独立的项目中定义的,不同于类。我宁愿避免制作该库的多个硬编码版本,一个用于使用它的每个程序中的每组类。

我想过在每个子类中都有一个特殊类的静态实例,这将在其构造函数中进行注册。但是,我发现没有办法保证容器会在这些对象的构造之前被构造。

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

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

【问题讨论】:

  • 您不能将注册表对象(您的地图)设为通过 getInstance() 动态创建的单例吗?

标签: c++ static-constructor static-initialization


【解决方案1】:

您同时描述了不同的问题。关于具有某种静态初始化的特定问题,一种简单的方法是创建一个假类来执行注册。那么每个不同的类都可以有一个static const X成员,这个成员必须在翻译单元中定义,定义会触发实例的实例化和类的注册。

这并没有解决难题,即初始化订单惨败。该语言不对不同翻译单元中对象的初始化顺序提供任何保证。也就是说,如果你用这样的类编译三个翻译单元,就不能保证假成员的相对执行顺序。这也适用于库:如果您要在其中注册类的容器是全局/静态成员属性,则无法保证已初始化该容器。

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

// 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... 类型的对象,那么在构造函数调用上的捎带注册也不是解决方案。

【讨论】:

  • 谢谢。这看起来像我正在寻找的东西。至于最后一件事,您是否指的是编译器可能会省略编译一个类,如果它不是在某个地方构造的?
  • 不是真的,而是因为如果没有关于你的代码做什么的信息,很难判断你是否真的在其他地方实例化了这些类型的对象。如果您的代码没有创建 a 的对象,那么将注册调用添加到 a 构造函数将没有任何效果 -- 没有调用构造函数 => 没有执行注册
【解决方案2】:

这违背了 OOP 范式,但是让你的静态成员形成一个由 2 个全局变量引导的链表怎么样?你可以这样做:

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

相信全局变量,因为它们不需要构造函数,而只是纯数据,保证在您开始执行时已经初始化你的代码。

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      一个想法是将 registration 函子传递给类。每个后代都将执行注册函数。这个函子可以在构造函数中传递。

      例子:

      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");
        }
      };
      

      【讨论】:

        【解决方案5】:

        见:http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14

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

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

        “模仿”静态构造函数的唯一方法是显式调用函数来执行静态初始化。没有其他方法可以仅通过在模块中链接来运行代码 pre-main。

        【讨论】:

          【解决方案6】:

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

          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 ;
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-05-06
            • 2011-10-07
            • 2011-04-19
            • 2012-12-10
            • 2014-03-14
            • 2014-07-13
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多