静态模板数据成员存储
首先,我将编写示例来正确解决问题。
首先,我将声明用于创建单例对象的模板(不是自动创建的): 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();
}
现在我有 Child1
和 Child2
拥有自己的 s_instance
并且它们将在给定时刻指向该类的唯一实例。
问题是关于这个静态数据成员s_instance
的存储。与常规静态数据成员不同,我没有指定它应该分配在哪里。当然,我希望在 child1.o 中有 SingletonBase
和 SingletonBase
和child2.o 分别,但这是我可以期望或强制执行的吗?
如果我将 Child1
和 Child2
放入两个不同的库 - lib1 和 lib2 中,问题会变得更加复杂。在 Child2::staticInvokeBoth()
内部有一个对 Child1::instance()
的调用。据我了解,默认 gcc 的行为是在每个编译单元中生成 SingletonBase
的副本,因此将在 lib2 中发出一个副本。
它还会在 lib2 中生成 SingletonBase
的副本吗?当然,SingletonBase
的一个副本应该位于 lib1 中。如果这两个库稍后在一个应用程序中一起使用,我能否确定只有一个 SingletonBase
实例,并且都有 Child1::staticInvokeOne()< /code> 和
Child2::staticInvokeBoth()
正在使用它?
使用这种方法将静态数据封装在模板中通常是安全的,还是有任何缺点?
先感谢您!
First I'll write example to properly address the question.
First of all, I'll declare template to be used to create singleton object (not auto-created):
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;
Now we can declare any class that derived from template, and each derived class should have its own s_instance. For example:
child1.h
class Child1 : public SingletonBase<Child1>
{
...
void doSomething();
static void staticInvokeOne();
};
child2.h
class Child2 : public SingletonBase<Child2>
{
...
void doSomethingElse();
static void staticInvokeBoth();
};
I also have Child's implementation in child1.cpp and child2.cpp respectively.
child1.cpp
void Child1::staticInvokeOne()
{
instance()->doSomething();
}
child2.cpp
void Child2::staticInvokeBoth()
{
Child1::instance()->doSomething();
instance()->doSomethingElse();
}
Now I have Child1
and Child2
having their own s_instance
and they will point to the only instance of that class at given moment.
The question is about storage for this static data member s_instance
. Unlike with regular static data members I have not specified where it should be allocated. I would, of course, like to have SingletonBase<Child1>::s_instance
and SingletonBase<Child2>::s_instance
in child1.o and child2.o respectively, but is that something I can expect or enforce?
The issue gets more complicated if I put Child1
and Child2
into two distinct libraries - lib1 and lib2. Inside Child2::staticInvokeBoth()
there is a call to Child1::instance()
. As far as I understand, default gcc's behaviour is to generate a copy of SingletonBase<Child1>::s_instance
in each compilation unit, thus one copy will be emitted in lib2.
Will it also generate a copy of SingletonBase<Child1>::s_instance
in lib2? Definitely, one copy of SingletonBase<Child1>::s_instance
should be in lib1. If those two libraries are later used together in one application, can I be sure that there is only one instance of SingletonBase<Child1>::s_instance
and that both Child1::staticInvokeOne()
and Child2::staticInvokeBoth()
are using it?
Is it generally safe to use this approach with statics wrapped in template, or are there any drawbacks?
Thank you in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对此的答案与任何其他基于模板或内联函数相同 - 唯一的区别是在这种情况下变量最终被标记为放置在读写部分中。
在大多数编译器中,编译器将在引用它们的每个编译单元中实例化任何所需的模板函数和静态成员变量。编译器还会将它们标记为“弱符号”;这意味着,在最后的链接阶段,链接器将任意选择发出的副本之一进入最终的可执行文件。
但请注意,弱符号合并过程通常在静态链接阶段完成。动态链接器将不会为您执行此操作。因此,您应该避免在共享库中使用可变(读写)模板静态数据成员。还要避免跨共享库(包括 RTTI 数据)对只读模板静态数据成员进行地址比较。
请记住,对于共享库,几乎任何跨越 ABI 边界的模板定义更改都会破坏您的 ABI - 因此最好完全避免在共享库 API 中使用模板!
The answer to this is the same as with any other template-based or inline function - the only difference is in this case the variable ends up being marked for placement in a read-write section.
In most compilers, the compiler will instantiate any needed template functions and static member variables in every compilation unit they are referenced in. The compiler will also mark these as 'weak symbols'; this means that, at the final link phase, the linker will choose one of the emitted copies arbitrarily to go into the final executable.
Note, however, that the weak symbol consolidation process is usually done at the static linking stage. The dynamic linker will not do this for you. As such, you should avoid having mutable (read-write) template static data members in shared libraries. Also avoid address comparisons on read-only template static data members across shared libraries (including RTTI data).
And keep in mind in general with shared libraries that almost any change in template definitions that cross the ABI boundrary will break your ABI - so it's probably best to avoid templates entirely in shared library APIs!
虽然我可能会误解你的问题,但如果你的问题中有存储
意味着定义,我的答案可能适用。
对于一项定义规则,该标准的3.2 p5说:
以及
这条规则有一些要求。在这个问题的情况下,
因为变量是
用积分常数0初始化,满足要求。
Though I may misunderstand your question, if storage in your question
means definition, my answer might apply.
As for one definition rule, 3.2 p5 of the standard says:
and
There are some requirements for this rule. In this question's case,
since the variable is
initialized by an integral constant 0, the requirements are satisfied.