【问题标题】:C++ static const variable and destructionC ++静态常量变量和破坏
【发布时间】:2009-06-19 12:41:33
【问题描述】:

我在一个简单的 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 字符串的内容会发生变化。这里有人能很好地解释该代码中发生了什么吗?

谢谢, 诺伯特

【问题讨论】:

标签: c++ constants static-members


【解决方案1】:

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

原答案:

你的意思是有


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

所以 CONST_STR 是 A 类的一部分?

否则您将单独声明它并且初始化A的静态成员。

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      g_aStuff constructor 
      CONST_STR constructor
      
      main funciton initializes g_aStuff
      
      CONST_str destructor 
      g_aStuff descrutor  
      

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

      【讨论】:

      • 很遗憾没有。代码确实编译得很好,缺少的 A:: 只是编写示例代码时的一种类型。这个例子很简单,我只是在这里的文本字段中写了它,并没有编译它。对不起。
      【解决方案4】:

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

      【讨论】:

      • +1 是正确的,但为什么构造函数中的测试通过了? A::CONSTR_STR 此时应该为空。
      • 这就是静态成员 CONSTR_STR 的意义所在。它是一个全局变量,在 main() 运行之前被初始化。
      • 哦,好吧,缺少范围分辨率只是一个错字。
      • 甚至在main运行之前不需要初始化:可以稍后初始化。 C++ 初始化规则是一场噩梦,真的
      【解决方案5】:

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

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

      但是,CONST_STR 在执行 A 的构造函数之前被初始化,因为它们在同一个翻译单元中。

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-07-14
            • 1970-01-01
            • 2018-04-22
            • 2012-05-12
            • 2013-06-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多