c++ 如何在header中初始化静态成员[duplicate]

x0fgdtte  于 2023-10-21  发布在  其他
关注(0)|答案(7)|浏览(156)

此问题已在此处有答案

How can you define a static data member of type const std::string?(11个回答)
How to initialize private static data members in a header file(18个回答)
8天前关闭
Given是一个具有静态成员的类。

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

显然,String必须在类外部进行 default-initialized

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

如果我将上面的行与类一起沿着包含在标题中,我会得到一个symbol multiply defined错误。它必须在单独的cpp文件中定义,即使是include guardspragma once
有没有办法在header中定义它?

kh212irz

kh212irz1#

不能多次定义static成员变量。如果你把变量定义放在一个头文件中,它将在包含头文件的每个翻译单元中定义。由于包含守卫只影响一个翻译单元的编译,它们也不会有帮助。
但是,您可以定义static成员函数!现在,乍一看,它可能没有帮助,当然,该函数可以有本地static变量,并返回对其中一个的引用,其行为几乎像static成员变量:

static std::string& bstring() { static std::string rc{"."}; return rc; }

本地变量static将在第一次调用此函数时初始化。也就是说,构造被延迟到第一次访问函数。当然,如果你使用这个函数初始化其他全局对象,它也可以确保对象是及时构造的。如果你使用多线程,这可能看起来像一个潜在的数据竞争,但它不是(除非你使用C++03):函数local static变量的初始化是线程安全的。

ujv3wf0j

ujv3wf0j2#

在C++17中,你可以使用 inline variables,甚至可以在 outside classes中使用。

  • 内联说明符,当在具有静态存储期限的变量(静态类成员或命名空间作用域变量)的decl-specifier-seq中使用时,声明该变量为内联变量。

声明为constexpr的静态成员变量(但不是命名空间作用域变量)隐式地是内联变量。
举例来说:

class Someclass {
public:
    inline static int someVar = 1;
};

或者

namespace SomeNamespace {
    inline static int someVar = 1;
}

联系我们

cwdobuhd

cwdobuhd3#

关于

**"**有没有办法在header中定义[static data member]?

是的。

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建议的那样。本质上,这是一个迈耶斯的单身(谷歌它)。

  • 编辑 *:另外,自从这个答案发布以来,我们已经得到了内联对象的建议,我认为C++17已经接受了。

无论如何,三思而后行关于这里的设计。全局变量是Evil™。这基本上是全球性的。

42fyovps

42fyovps4#

在C++11中,为了保持静态值的定义与声明,可以使用嵌套的静态结构。在这种情况下,静态成员是一个结构,必须在.cpp文件中定义,但值在头文件中。

class BaseClass
{
public:
  static struct _Static {
     std::string bstring {"."};
  } global;
};

不是初始化单个成员,而是初始化整个静态结构:

BaseClass::_Static BaseClass::global;

这些值是通过

BaseClass::global.bstring;

请注意,该解决方案仍然存在静态变量初始化顺序的问题。当一个静态值被用来初始化另一个静态变量时,第一个变量可能还没有被初始化。

// file.h
class File {
public:
  static struct _Extensions {
    const std::string h{ ".h" };
    const std::string hpp{ ".hpp" };
    const std::string c{ ".c" };
    const std::string cpp{ ".cpp" };
  } extension;
};

// file.cpp
File::_Extensions File::extension;

// module.cpp
static std::set<std::string> headers{ File::extension.h, File::extension.hpp };

在这种情况下,静态变量 headers 将包含{“”}或{“.h”,“.hpp”},这取决于链接器创建的初始化顺序。

2skhul33

2skhul335#

§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的实体定义在多个翻译单元中,那么[...]
显然,类类型的静态数据成员的定义不被认为出现在多个翻译单元中。因此,* 根据标准,这是不允许的 *。
来自Cheers和hth的建议答案。- Alf和Dietmar更像是一种“黑客”,利用了
类模板的静态数据成员(14.5.1.3)

带外部链接的内联函数(7.1.2)
允许在多个TU中使用(仅供参考:在类定义内部定义的静态函数具有外部链接,并且被隐式地定义为内联)。

vsaztqbk

vsaztqbk6#

不,它不能在头文件中完成-至少如果头文件不止一次包含在源文件中,这似乎是事实,否则你不会得到这样的错误。只要把它放在一个.cpp文件中就可以了。

mftmpeh8

mftmpeh87#

更新:我下面的回答解释了为什么这不能以问题所建议的方式完成。至少有两个答案可以回避这一点;他们可能会解决问题,也可能不会。
bstring静态成员必须链接到特定的内存地址。要做到这一点,它必须出现在单个对象文件中,因此它必须出现在单个cpp文件中。除非你使用#ifdef来确保这一点,否则你想要的东西不能在头文件中完成,因为你的头文件可能包含在多个cpp文件中。

相关问题