我遇到了一个简单的C ++类的奇怪行为。
classA.h
class A
{
public:
A();
~A();
static const std::string CONST_STR;
};
classA.cpp
#include "classA.h"
#include <cassert>
const std::string A::CONST_STR("some text");
A::A()
{
assert(!CONST_STR.empty()); //OK
}
A::~A()
{
assert(!CONST_STR.empty()); //fails
}
的main.cpp
#include <memory>
#include <classA.h>
std::auto_ptr<A> g_aStuff;
int main()
{
//do something ...
g_aStuff = std::auto_ptr<A>(new A());
//do something ...
return 0;
}
我希望访问违规或类似的东西,但我永远不会期望静态const字符串的内容可能会改变。这里有没有人能够很好地解释该代码中会发生什么?
感谢, 诺贝特
答案 0 :(得分:3)
编辑:显然,丢失的A::
是代码原始帖子中的拼写错误。
原始答案:
你的意思是
const std::string A::CONST_STR("some text");
这样CONST_STR就是类A
的一部分?
否则,您将单独声明它并且不初始化A
的静态成员。
答案 1 :(得分:2)
您在两个不同的编译单元中创建了2个静态变量。无法判断它们的初始化顺序。但是他们的析构函数总是以相反的顺序调用。
在您的情况下,似乎发生了下一个场景:
g_aStuff constructor
CONST_STR constructor
main funciton initializes g_aStuff
CONST_str destructor
g_aStuff descrutor
此时CONST_STR.empty()的结果未定义。这可能会触发断言。
答案 2 :(得分:2)
我希望访问违规或 类似的东西,但我永远不会想到 那个静态const的内容 字符串可能会改变。
未定义的行为:未定义。如果CONST_STR已被销毁,那么如果您访问它,则无法保证硬件异常。它可能会崩溃,但是它的地址可能会最终包含看起来像空字符串的数据:它的析构函数可能会清除指针或其他内容。
在这种情况下,您说A实例也存储在全局智能指针中,该指针在main()中指定。所以CONST_STR是在A构造函数中访问时构造的,但很可能在智能指针被销毁之前被销毁。我们需要整个计划肯定地说。
[编辑:你做到了。由于CONST_STR和g_aStuff是在不同的编译单元中定义的,因此它们的相对构造顺序不是由标准定义的。我猜测CONST_STR首先被销毁。]
答案 3 :(得分:1)
classA.cpp中定义的
const std::string CONST_STR("some text");
不是A的成员。该定义如下所示:
const std::string A::CONST_STR("some text");
答案 4 :(得分:1)
标准未指定不同翻译单元中全局/静态对象的初始化顺序。但是,它确保在执行该翻译单元的任何功能之前初始化每个这样的对象。
在你的情况下,CONST_STR
在g_aStuff
之后被初始化,并且由于破坏的顺序与构造顺序相反,它在它之前被销毁。因此,从CONST_STR
的析构函数访问A
会调用未定义的行为 - 您可能会遇到访问冲突,或者您可能没有。
CONST_STR
在A
的构造函数执行之前被初始化,因为它们位于相同的翻译单元中。
答案 5 :(得分:0)
如果存在A的全局实例(或类型A的静态类成员),则可能会发生这种情况。由于没有定义全局变量和静态的初始化顺序(交叉翻译单位),它可以是。
答案 6 :(得分:0)
查看完整代码,您将依赖于编译单元(classA.cpp和main.cpp)的销毁顺序。如果你在main中创建g_aStuff作为本地,你的断言应该通过。