我很久以前就知道,唯一可靠的方法是初始化一个静态成员肯定是在一个函数中做。
不,不是。该标准保证:
- 所有带有普通构造函数的静态存储(块和文件或类静态范围)的对象都会在任何代码运行之前初始化。程序的任何代码。
- 所有具有文件/全局/类静态范围和非平凡构造的对象都在
main
调用函数之前进行初始化。可以保证,如果对象 A 和 B 在同一个翻译单元中定义并且 A 在 B 之前定义,则 A 在 B 之前初始化。但是,在不同翻译单元中定义的对象的构造顺序是未指定的,并且在编译之间通常会有所不同。
- 任何块静态对象在第一次达到其声明时都会被初始化。由于 C++03 标准不支持线程,这不是线程安全的!
- 在函数退出或应用程序使用系统调用终止后,所有具有静态存储的对象(块和文件/全局/类静态范围)都按照其构造函数完成的相反顺序销毁。
main()
exit()
这两种方法在所有情况下都不是可用且可靠的!
现在,我要做的是开始通过非常量引用返回静态数据,我需要有人阻止我。
没有人会阻止你。这是合法且完全合理的事情。但请确保您不会陷入线程陷阱。
例如,任何合理的 C++ 单元测试库都会自动注册所有测试用例。它通过以下方式实现:
std::vector<TestCase *> &testCaseList() {
static std::vector<TestCase *> test_cases;
return test_cases;
}
TestCase::TestCase() {
...
testCaseList().push_back(this);
}
因为这是仅有的两种方法之一。另一个是:
TestCase *firstTest = NULL;
class TestCase {
...
TestCase *nextTest;
}
TestCase::TestCase() {
...
nextTest = firstTest;
firstTest = this;
}
这次使用firstTest
具有平凡构造函数的事实,因此将TestCase
在任何具有非平凡构造的 s 之前初始化。
数据槽() = 7; // 完全正常?
是的。但如果你真的想要,你可以这样做:
旧 C 的东西
#define dataSlot _dataSlot()
以某种方式errno
通常定义“变量”,
或者你可以将它包装在一个结构中,比如
class dataSlot {
Type &getSlot() {
static Type slot;
return slot;
}
operator const Type &() { return getSlot(); }
operator=(Type &newValue) { getSlot() = newValue; }
};
(这里的缺点是如果您尝试直接在 dataSlot 上调用 Type 的方法,编译器不会查找它们;这就是它需要 operator= 的原因)