静态模板数据成员存储

nyr*_*yrl 2 c++ templates static-members

首先,我会写一些例子来正确解决这个问题.

首先,我将声明用于创建单例对象的模板(不是自动创建的): singleton_base.h

template <class Derived>
class SingletonBase
{
  public:
    static Derived* instance() { assert(s_instance); return dynamic_cast<Derived*>(s_instance); }
  protected:
    SingletonBase() { assert(s_instance==0); s_instance=this; }
    virtual ~SingletonBase() { assert(s_instance); s_instance=0; }
  private:
    static SingletonBase* s_instance;
};

template <class Derived>
SingletonBase<Derived>* SingletonBase<Derived>::s_instance = 0;
Run Code Online (Sandbox Code Playgroud)

现在我们可以声明从模板派生的任何类,并且每个派生类都应该有自己的s_instance.例如:

child1.h

class Child1 : public SingletonBase<Child1>
{
  ...
  void doSomething();
  static void staticInvokeOne();
};
Run Code Online (Sandbox Code Playgroud)

child2.h

class Child2 : public SingletonBase<Child2>
{
  ...
  void doSomethingElse();
  static void staticInvokeBoth();
};
Run Code Online (Sandbox Code Playgroud)

我也分别在child1.cppchild2.cpp中实现了Child的实现.

child1.cpp

void Child1::staticInvokeOne()
{
  instance()->doSomething();
}
Run Code Online (Sandbox Code Playgroud)

child2.cpp

void Child2::staticInvokeBoth()
{
  Child1::instance()->doSomething();
  instance()->doSomethingElse();
}
Run Code Online (Sandbox Code Playgroud)

现在我已经Child1Child2拥有自己的s_instance,他们将指向类在给定时刻的唯一实例.

问题是关于此静态数据成员的存储s_instance.与常规静态数据成员不同,我没有指定应该在哪里分配它.当然,我希望分别拥有SingletonBase<Child1>::s_instanceSingletonBase<Child2>::s_instancechild1.ochild2.o,但这是我可以期待或执行的东西吗?

这个问题得到,如果我把更复杂Child1,并Child2为两个不同的库- LIB1LIB2.在里面Child2::staticInvokeBoth()有一个电话Child1::instance().据我所知,默认gcc的行为是SingletonBase<Child1>::s_instance在每个编译单元中生成一个副本,因此将在lib2中发出一个副本.

它也将产生的副本SingletonBase<Child1>::s_instanceLIB2?当然,一个副本SingletonBase<Child1>::s_instance应该在lib1中.如果这两个库后来在一个应用程序一起使用,我可以肯定的是,只有一个实例SingletonBase<Child1>::s_instance,并且两个Child1::staticInvokeOne()Child2::staticInvokeBoth()使用它?

使用这种方法将模板包装在模板中是否通常是安全的,还是有任何缺点?

先感谢您!

bdo*_*lan 5

对此的答案与任何其他基于模板或内联函数的答案相同 - 唯一的区别是在这种情况下变量最终被标记为放置在读写部分中.

在大多数编译器中,编译器将在引用它们的每个编译单元中实例化任何所需的模板函数和静态成员变量.编译器还会将这些标记为"弱符号"; 这意味着,在最后的链接阶段,链接器将任意选择一个发出的副本进入最终的可执行文件.

但请注意,弱符号合并过程通常在静态链接阶段完成.动态链接器不会为您执行此操作.因此,您应该避免在共享库中使用可变(读写)模板静态数据成员.还要避免跨共享库(包括RTTI数据)对只读模板静态数据成员进行地址比较.

一般而言,请记住共享库,跨越ABI边界的模板定义中的任何变化几乎都会破坏您的ABI - 因此最好完全避免在共享库API中使用模板!