Han*_* S. 14 c++ initialization linkage
在阅读了很多关于静态变量初始化的问题之后,我仍然不确定它如何适用const
于命名空间级别的变量.
我在构建脚本生成的头文件中有以下代码config.h
:
static const std::string path1 = "/xyz/abc";
static const std::string path2 = "/etc";
Run Code Online (Sandbox Code Playgroud)
根据我所读到的,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");
Run Code Online (Sandbox Code Playgroud)
这会引起静态初始化的任何问题吗?
如果我理解正确,由于const
变量具有内部联系,两种情况都应该没有问题?
编辑:(由于运动答案)
也许我应该提一下,我对以下用例感兴趣:
在main.cpp
:
#include <config.h>
#include <myclass.h>
std::string anotherString(path1 + myclass1.Get());
int main()
{
...
}
Run Code Online (Sandbox Code Playgroud)
关于这个用例的另一个问题:path2
在这种情况下,编译器会优化吗?
Phi*_*ipp 12
您的第一个定义path1
位于包含的每个编译单元中config.h
.为避免这种情况,请不要在头文件中定义变量.通常你会在标题中声明变量extern
:
extern const std::string path1;
extern const MyClass myclass1;
Run Code Online (Sandbox Code Playgroud)
并在翻译单元中定义它们,例如config.cpp
:
const std::string path1 = "/xyz/abc";
const MyClass myclass1("test");
Run Code Online (Sandbox Code Playgroud)
有时您需要一个只能从一个翻译单元使用的常量变量.然后,您可以将文件范围内的变量声明为static
.
static const std::string path1 = "/xyz/abc";
Run Code Online (Sandbox Code Playgroud)
static
不再被弃用了.static
并且extern
有时暗示,但我总是忘记在哪里以及如何,所以我通常明确地为所有命名空间级变量指定它们.
我试图从C++ 03标准文档中获取必要的信息.这是我发现的:
关于const static
声明:
根据3.5.3节在命名空间级别定义的对象,默认情况下声明const
具有内部链接.static
还声明了一个名称空间级别对象,以便具有内部链接,因此不需要声明对象static const
.
还根据附件D.2
在命名空间范围内声明对象时,不推荐使用static关键字(参见3.3.5).
关于静态初始化fiasco:
由于变量是在头文件中定义的,因此它们总是在使用它们的任何其他静态对象之前定义.
从第3.6.2.1节:
在同一翻译单元的命名空间范围内定义并动态初始化的静态存储持续时间的对象应按其定义出现在翻译单元中的顺序进行初始化.
答案1:这意味着将变量传递给静态对象构造应该没问题.
答案2:但是,如果变量是从静态对象的非内联构造函数引用的,则可能会出现问题:
如果在第一个语句之前完成动态初始化,则在3.6.2.1和3.6.2.3中都没有指定不同编译单元中的静态对象的初始化顺序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"
}
Run Code Online (Sandbox Code Playgroud)
由于myclass.cpp
它有自己的const
变量副本,因此在MyClass::MyClass()
调用时可能不会初始化这些变量.
所以是的,const
头文件中定义的变量可以以易于静态初始化惨败的方式使用
据我所知,这仅适用于不需要静态初始化的变量:
从C++ 03标准,第3.6.2.1节:
具有使用常量表达式(5.19)初始化的静态存储持续时间的POD类型(3.9)的对象应在任何动态初始化发生之前初始化.
当一个命名空间级别变量依赖于分配给之前可能或未初始化的不同命名空间级别变量的值时,所谓的静态初始化失败是一个问题.在你的两个例子中没有这样的依赖,并且应该没有任何问题.
另一方面,这容易出现这种类型的错误:
// 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;
}
Run Code Online (Sandbox Code Playgroud)
即使两者都是不变的,也无法保证foo
之前会被初始化foobar
.这意味着程序行为是未定义的,它可以很好地打印"foobar","bar"或者死亡.