14

在阅读了很多关于静态变量初始化的问题之后,我仍然不确定这如何应用于const命名空间级别的变量。

我在构建脚本生成的文件中有以下代码:config.h

static const std::string path1 = "/xyz/abc";
static const std::string path2 = "/etc";

根据我所读到的static关键字是没有必要的,甚至在这里不推荐使用。

我的问题:上面的代码是否容易出现静态初始化失败?

如果我在文件中有以下内容myclass.h

class MyClass
{
public:
    MyClass(const std::string& str) : m_str(str) {}
    std::string Get() const { return m_str; }

private:
    std::string m_str;
}

const MyClass myclass1("test");

这会对静态初始化造成任何问题吗?

如果我理解正确,由于const变量具有内部联系,这两种情况都应该没有问题?

编辑:(由于dribeas回答)

也许我应该提到我对以下用例感兴趣:

main.cpp

#include <config.h>
#include <myclass.h>

std::string anotherString(path1 + myclass1.Get());

int main()
{
    ...
}

关于这个用例的另一个问题:path2在这种情况下编译器会优化掉吗?

4

4 回答 4

13

您的第一个定义放在path1包含config.h. 为避免这种情况,请不要在头文件中定义变量。通常您会将标头中的变量声明为extern

extern const std::string path1;
extern const MyClass myclass1;

并在翻译单元中定义它们,例如config.cpp

const std::string path1 = "/xyz/abc";
const MyClass myclass1("test");

有时您需要一个只能在一个翻译单元中使用的常量变量。然后,您可以在文件范围内将该变量声明为static.

static const std::string path1 = "/xyz/abc";

static不再被弃用。static并且extern有时是隐含的,但我总是忘记在哪里以及如何使用,所以我通常为所有命名空间级别的变量明确指定它们。

于 2011-01-26T15:52:13.007 回答
11

我试图从 C++03 标准文档中获取必要的信息。这是我发现的:

关于const static声明:

根据第 3.5.3 节,在命名空间级别定义并声明的对象默认const具有内部链接static还声明了一个命名空间级别的对象以具有内部链接,因此无需声明对象static const

同样根据附件 D.2

在命名空间范围内声明对象时,不推荐使用 static 关键字(参见 3.3.5)。

关于静态初始化惨败:

由于变量是在头文件中定义的,因此它们总是在使用它们的任何其他静态对象之前定义。

从第 3.6.2.1 节开始:

在同一翻译单元的命名空间范围内定义的静态存储持续时间并动态初始化的对象应按照其定义在翻译单元中出现的顺序进行初始化。

答案 1:这意味着变量传递给静态对象构造函数应该没问题。

答案 2:但是,如果变量是从静态对象的非内联构造函数中引用的,则可能会出现问题:

如果动态初始化在main.

考虑以下:

// consts.h
#include <string>

const std::string string1 = "ham";
const std::string string2 = "cheese";

// myclass.h
#include <string>

class MyClass
{
public:
    MyClass();
    MyClass(std::string str);
    std::string Get() { return memberString; }
private:
    std::string memberString;
}

// myclass.cpp
#include "consts.h"
#include "myclass.h"

MyClass::MyClass() : memberString(string1) {}

MyClass::MyClass(std::string str) : memberString(str) {}

// main.cpp
#include <iostream>
#include "consts.h"
#include "myclass.h"

MyClass myObject1;
MyClass myObject2(string2);

using namespace std;

int main()
{
    cout << myObject1.Get(); // might not print "ham"
    cout << myObject2.Get(); // will always print "cheese"
}

由于myclass.cpp有自己的变量副本,因此在调用const时可能不会初始化这些变量。MyClass::MyClass()

所以是的,const头文件中定义的变量可以以容易导致静态初始化失败的方式使用

据我所知,这仅适用于不需要静态初始化的变量:

从 C++03 标准,第 3.6.2.1 节:

使用常量表达式 (5.19) 初始化的具有静态存储持续时间的 POD 类型 (3.9) 的对象应在任何动态初始化发生之前进行初始化。

于 2011-01-27T15:02:28.147 回答
8

当一个命名空间级别变量依赖于分配给不同命名空间级别变量的值时,所谓的静态初始化失败是一个问题,该变量之前可能会或未初始化。在您的两个示例中,没有这种依赖关系,也不应该有任何问题。

另一方面,这很容易出现这种类型的错误:

// header.h
extern const std::string foo;

// constant.cpp
const std::string foo( "foo" );

// main.cpp
#include "header.h"
const std::string foobar( foo+"bar" );
int main() {
   std::cout << foobar << std::endl;
}

不保证foo会在 之前初始化foobar,即使两者都是常量。这意味着程序行为是未定义的,它可以很好地打印“foobar”、“bar”或死亡。

于 2011-01-26T15:54:20.227 回答
2

静态初始化惨败是指相互依赖的静态变量。仅仅定义一些static const变量不会成为问题的根源。

于 2011-01-26T15:55:35.220 回答