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

App*_*ell 35 c++ class static-members

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

class BaseClass
{
public:
    static std::string bstring;
};
Run Code Online (Sandbox Code Playgroud)

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

std::string BaseClass::bstring {"."};
Run Code Online (Sandbox Code Playgroud)

如果我在标题中包含上面的行以及类,我会收到symbol multiply defined错误.它必须在单独的cpp文件中定义,即使使用include guardspragma once.

有没有办法在标题中定义它?

Die*_*ühl 58

您不能static多次定义成员变量.如果将变量定义放入标题中,则将在包含标题的每个转换单元中定义它.由于包含警卫只影响一个翻译单元的编译,他们也无济于事.

但是,您可以定义static成员函数!现在,乍一看,它可能看起来不像它可能会有所帮助,当然,除了该函数可以有局部static变量并返回对这些行为之一的引用,就像static成员变量一样:

static std::string& bstring() { static std::string rc{"."}; return rc; }
Run Code Online (Sandbox Code Playgroud)

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


And*_*and 18

在 C++17 中,您可以使用内联变量,甚至可以外部使用。

内联说明符在具有静态存储期的变量(静态类成员或命名空间范围变量)的声明说明符序列中使用时,将变量声明为内联变量。

声明为 constexpr 的静态成员变量(但不是命名空间范围的变量)隐式为内联变量。?¹?

例如:

class Someclass {
public:
    inline static int someVar = 1;
};
Run Code Online (Sandbox Code Playgroud)

或者,

namespace SomeNamespace {
    inline static int someVar = 1;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 您能详细说明一下声明内联变量的后果吗?它会被初始化一次(像所有全局初始化数据一样)吗?我假设该对象只有一个实例,但是这是如何跨翻译单元工作的(当实现发生变化时,内联有时会阻止用函数定义替换 DLL,IIRC)。 (6认同)

Che*_*Alf 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>
{};
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用一种功能,正如Dietmar建议的那样.基本上这是迈耶斯的单身人士(google it).

编辑:此外,由于这个答案已经发布,我们已经有了内联对象提案,我认为这个提议已被C++ 17接受.

无论如何,在这里三思而后行.Globals变量是Evil™.这基本上是一个全球性的.


Mar*_*nič 5

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

class BaseClass
{
public:
  static struct _Static {
     std::string bstring {"."};
  } global;
};
Run Code Online (Sandbox Code Playgroud)

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

BaseClass::_Static BaseClass::global;
Run Code Online (Sandbox Code Playgroud)

这些值通过访问

BaseClass::global.bstring;
Run Code Online (Sandbox Code Playgroud)

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

// 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 };
Run Code Online (Sandbox Code Playgroud)

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