1

我在一个简单的 C++ 类中遇到了一个奇怪的行为。

类A.h

class A
{
public:
  A();
  ~A();
  static const std::string CONST_STR;
};

类A.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
}

主文件

#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 字符串的内容会改变。这里有没有人能很好地解释该代码中发生了什么?

谢谢,诺伯特

4

7 回答 7

3

编辑:显然缺少A::的是代码原始帖子中的错字。

原答案:

你的意思是有


    const std::string A::CONST_STR("some text");

所以 CONST_STR 是类的一部分A

否则,您将单独声明它,而不是初始化A.

于 2009-06-19T12:50:30.527 回答
2

您正在两个不同的编译单元中创建 2 个静态变量。无法判断它们的初始化顺序。但是它们的析构函数总是以相反的顺序调用。

在您的情况下,似乎发生了下一个场景:

g_aStuff constructor 
CONST_STR constructor

main funciton initializes g_aStuff

CONST_str destructor 
g_aStuff descrutor  

此时 CONST_STR.empty() 的结果未定义。这可能会触发断言。

于 2009-06-19T12:53:50.287 回答
2

我预计访问冲突或类似的事情,但我从没想过静态 const 字符串的内容会改变。

未定义的行为:它是未定义的。如果 CONST_STR 已被销毁,那么如果您访问它,则不能保证出现硬件异常。它可能会崩溃,但它的地址可能最终会包含看起来像一个空字符串的数据:它的析构函数可能会清除指针或其他任何东西。

在这种情况下,您说 A 实例也存储在全局智能指针中,该指针在 main() 中分配。所以 CONST_STR 在 A 构造函数中被访问时已经被构造,但很可能在智能指针被销毁之前被销毁。我们需要整个程序来肯定地说。

[编辑:你已经做到了。由于 CONST_STR 和 g_aStuff 定义在不同的编译单元中,因此标准没有定义它们的相对构造顺序。我猜是 CONST_STR 首先被销毁。]

于 2009-06-19T13:00:20.570 回答
1

const std::string CONST_STR("some text");
classA.cpp 中定义的不是 A 的成员。该定义如下所示:

const std::string A::CONST_STR("some text");
于 2009-06-19T12:51:40.777 回答
1

该标准没有指定不同翻译单元中全局/静态对象的初始化顺序。但是,它确实保证每个这样的对象都将在执行该翻译单元的任何函数之前被初始化。

在您的情况下,它发生在CONST_STR初始化之后g_aStuff,并且由于破坏顺序与构造顺序相反,因此它在它之前被破坏。因此,CONST_STRA的析构函数访问会调用未定义的行为——您可能会遇到访问冲突,也可能不会。

CONST_STR is, however, initialized before A's constructor is executed because they are in the same translation unit.

于 2009-06-19T13:04:38.830 回答
0

如果存在 A 的全局实例(或类型 A 的静态类成员),则可能会发生这种情况。由于未定义全局变量和静态变量的初始化顺序(跨翻译单元),因此可以。

于 2009-06-19T12:47:45.073 回答
0

查看完整代码,您依赖于跨编译单元(classA.cpp 和 main.cpp)的破坏顺序。如果您将 g_aStuff 创建为 main 中的本地,则您的断言应该通过。

于 2009-06-19T13:03:19.287 回答