如何初始化标头中的静态成员

时间:2013-09-17 22:25:16

标签: c++ class static-members

给定是一个具有静态成员的类。

class BaseClass
{
public:
    static std::string bstring;
};

字符串显然是在类之外默认初始化

std::string BaseClass::bstring {"."};

如果我在标题中包含上述行以及类,则会出现symbol multiply defined错误。它必须在单独的cpp文件中定义,即使使用include guardspragma once也是如此。

是否有办法在标题中定义它?

7 个答案:

答案 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]

⁽¹⁾https://en.cppreference.com/w/cpp/language/inline

答案 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)

  

带外部链接的内联函数(7.1.2)

多个TU中允许

(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};
};