静态模板数据成员存储

时间:2011-02-07 17:02:49

标签: 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;

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

child1.h

class Child1 : public SingletonBase<Child1>
{
  ...
  void doSomething();
  static void staticInvokeOne();
};

child2.h

class Child2 : public SingletonBase<Child2>
{
  ...
  void doSomethingElse();
  static void staticInvokeBoth();
};

我也分别在 child1.cpp child2.cpp 中实施了Child。

child1.cpp

void Child1::staticInvokeOne()
{
  instance()->doSomething();
}

child2.cpp

void Child2::staticInvokeBoth()
{
  Child1::instance()->doSomething();
  instance()->doSomethingElse();
}

现在我Child1Child2拥有自己的s_instance,他们会在特定时刻指向该课程的唯一实例。

问题是关于此静态数据成员s_instance的存储。与常规静态数据成员不同,我没有指定应该在哪里分配它。当然,我希望分别在 child1.o child2.o 中设置SingletonBase<Child1>::s_instanceSingletonBase<Child2>::s_instance,但这是我可以做的事情期待还是强制执行?

如果我将Child1Child2放入两个不同的库 - lib1 lib2 ,问题会变得更加复杂。在Child2::staticInvokeBoth()内,可以调用Child1::instance()。据我所知,默认gcc的行为是在每个编译单元中生成SingletonBase<Child1>::s_instance的副本,因此将在 lib2 中发出一个副本。

它还会在 lib2 中生成SingletonBase<Child1>::s_instance的副本吗?当然,SingletonBase<Child1>::s_instance的一个副本应该在 lib1 中。如果这两个库稍后在一个应用程序中一起使用,我是否可以确定只有SingletonBase<Child1>::s_instance的{​​{1}}实例并且Child1::staticInvokeOne()Child2::staticInvokeBoth()都在使用它?

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

提前谢谢!

2 个答案:

答案 0 :(得分:5)

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

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

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

一般情况下,请记住共享库,跨越ABI边界的模板定义几乎所有变化都会破坏您的ABI - 所以最好完全避免模板在共享库API中使用!

答案 1 :(得分:0)

虽然我可能会误解您的问题,但如果存储在您的问题中 表示定义,我的回答可能适用 至于一个定义规则,3.2 p5的标准说:

  

可以有多个定义   of ...类的静态数据成员   模板...在提供的程序中   每个定义出现在一个   不同的翻译单位,

  

如果D的定义满足所有   这些要求,然后是程序   应该表现得好像有一个单一的   D的定义。

此规则有一些要求。在这个问题的情况下, 因为变量是 通过积分常数0初始化,满足要求。