5

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

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

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

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

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

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

4

6 回答 6

2

您同时描述了不同的问题。在进行某种静态初始化的特定问题上,一种简单的方法是创建一个将执行注册的假类。然后每个不同的类都可以有一个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

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

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

这可能是解决方案,也可能不是解决方案,具体取决于其他代码是否创建该类型的对象。在工厂函数的特定情况下(想到的第一个),如果不允许创建类型的对象ab......那么在构造函数调用上捎带注册也不是解决方案。

于 2011-03-15T18:55:52.387 回答
1

它违反了 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

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

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

于 2011-03-15T19:00:53.013 回答
0

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

于 2011-03-15T18:47:22.333 回答
0

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

例子:

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");
  }
};
于 2011-03-15T18:50:12.560 回答
0

见: 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。

于 2011-03-15T18:55:26.693 回答
0

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

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 ;
于 2011-03-15T19:28:07.260 回答