给定是一个具有静态成员的类。
class BaseClass
{
public:
static std::string bstring;
};
字符串显然是在类之外默认初始化。
std::string BaseClass::bstring {"."};
如果我在标题中包含上述行以及类,则会出现symbol multiply defined
错误。它必须在单独的cpp
文件中定义,即使使用include guards
或pragma once
也是如此。
是否有办法在标题中定义它?
答案 0 :(得分:55)
您不能多次定义static
成员变量。如果将变量定义放入标题中,则将在包含标题的每个转换单元中定义它。由于包含警卫仅影响一个翻译单元的编译,因此它们也无济于事。
但是,您可以定义static
成员功能!现在,乍看起来可能看起来并不像它可能会有所帮助,当然,除了该函数可以有本地static
变量并且返回对这些行为之一的引用,就像static
成员变量一样:
static std::string& bstring() { static std::string rc{"."}; return rc; }
第一次调用此函数时,将初始化本地static
变量。也就是说,构造被延迟直到第一次访问该功能。当然,如果使用此函数初始化其他全局对象,它还可以确保对象是及时构造的。如果你使用多个线程,这可能看起来像一个潜在的数据竞争,但它不是(除非你使用C ++ 03):函数local static
变量的初始化是线程安全的。
答案 1 :(得分:10)
关于
“是否有办法在标题中定义[静态数据成员]?
是的。
template< class Dummy >
struct BaseClass_statics
{
static std::string bstring;
};
template< class Dummy >
std::string BaseClass_statics<Dummy>::bstring = ".";
class BaseClass
: public BaseClass_statics<void>
{};
另一种方法是使用一种功能,正如Dietmar建议的那样。基本上这是迈耶斯&#39;单身(谷歌)。
编辑:此外,由于此答案已发布,我们已获得内联对象提案,我认为该提案已被C ++ 17接受。
无论如何,三思而后行这里的设计。 Globals变量是Evil™。这本质上是一个全球性的。
答案 2 :(得分:4)
在C ++ 17中,您可以使用内联变量,甚至可以使用 outside 类。
内联说明符在具有静态存储持续时间的变量(静态类成员或命名空间范围变量)的decl-specifier-seq中使用时,将其声明为内联变量。
声明为constexpr的静态成员变量(但不是名称空间范围的变量)隐式是内联变量。⁽⁽>
例如:
long
或者,
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
at accountservice.adapter.batch.testJob.SyncDriverPartitioner.getLongValueFromMap(SyncDriverPartitioner.java:78) ~[classes/:?]
at accountservice.adapter.batch.testJob.SyncDriverPartitioner.partition(SyncDriverPartitioner.java:47) ~[classes/:?]
at accountservice.adapter.batch.testJob.SyncDriverPartitioner$$FastClassBySpringCGLIB$$6f3315e4.invoke(<generated>) ~[classes/:?]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.18.RELEASE.jar:4.3.18.RELEASE]
答案 3 :(得分:3)
不,它不能在标题中完成 - 至少如果标题在源文件中包含多次,这似乎是这种情况,或者你不会得到这样的错误。只需将其粘贴在其中一个.cpp文件中即可完成。
答案 4 :(得分:3)
§3.2.6
以及当前c ++ 17草案(n4296)中的以下段落定义了当不同翻译单元中存在多个定义时的规则:
类类型(第9条),枚举类型(7.2),内联函数可以有多个定义 外部链接(7.1.2),类模板(第14章),非静态函数模板(14.5.6),静态数据成员 类模板(14.5.1.3),类模板的成员函数(14.5.1.1)或模板特化 其中一些模板参数未指定(14.7,14.5.5),在程序中提供了每个定义 出现在不同的翻译单元中,并且定义满足以下要求。特定 这样一个名为D的实体在多个翻译单元中定义,然后[...]
显然,类型的静态数据成员的定义不被视为出现在多个翻译单元中。因此,根据标准,不允许。
干杯和赫斯的建议答案。 - Alf和Dietmar更像是一种“黑客”,利用了
的定义类模板的静态数据成员(14.5.1.3)
和
多个TU中允许带外部链接的内联函数(7.1.2)
(FYI:在类定义中定义的静态函数具有外部链接,并隐式定义为内联)。
答案 5 :(得分:1)
更新:我在下面的回答解释了为什么不能以问题所建议的方式做到这一点。绕过这个至少有两个答案;他们可能会也可能不会解决问题。
bstring
静态成员必须链接到特定的内存地址。为此,它必须出现在单个对象文件中,因此必须出现在单个cpp
文件中。除非您正在使用#ifdef
来确保发生这种情况,否则无法在头文件中执行您想要的操作,因为您的头文件可能包含在多个cpp
文件中。
答案 6 :(得分:-1)
如果初始化程序可以表示为文字,则可以在C ++ 11中解决:
inline std::string operator"" _s(const char* p, size_t n) {
return std::string{p, n};
}
class BaseClass {
// inline initialization using user-defined literals
// should allow for multiple definitions
std::string bstring{"."_s};
};