C++静态初始化顺序混乱问题

3

我目前正在学习C++,遇到了一些麻烦。

我使用了很多#define来开发程序,但我想使用static const代替(碰撞/类型/作用域... )。

所以,现在我的代码看起来像这样:

file1.hpp

 class A {
   public:
     static const std::string MY_CONST_VAR;
 };

file1.cpp

 const std::string A::MY_CONST_VAR = "some string";

file2.cpp

 static std::string arrayOfString[] = {
   A::MY_CONST_VAR,
   ...
  };

我的代码使用-W -Wall -Wextra -Werror标志编译时没有警告/错误。

然而,当我尝试运行它时,会导致段错误。

我已经使用valgrind运行它,并得到以下输出:

==11239== Invalid read of size 4
==11239==    at 0x5F525CB: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==11239==    by 0x40D076: _GLOBAL__sub_I__ZN16GraphicInterface13DEFAULT_WIDTHE (GraphicInterface.cpp:42)
==11239==    by 0x51AC7C: __libc_csu_init (in /home/simon/PSU_2013_zappy/gui/gui_zappy)
==11239==    by 0x66D8E54: (below main) (libc-start.c:246)
==11239==  Address 0xfffffffffffffff8 is not stack'd, malloc'd or (recently) free'd

因此,在arrayOfString实例化期间发生了段错误。我认为问题在于在常量之前分配了arrayOfInt。但是在这种情况下,是否可以使用静态const来达到这个目的?

我不知道如何修补这个问题。我做错了吗?有更好的方法吗?如何解决这个问题?


5
请查看静态初始化顺序混乱的常见问题解答(FAQ)。 - Cheers and hth. - Alf
2
你能使用constexpr吗?如果可以,将常量声明为constexpr可能会解决你的问题。 - oblitum
1
通常情况下,您应该尽量避免在C++中(在文件作用域)动态初始化不是自包含的静态变量,即使用调用外部系统的构造函数初始化静态变量。std::string调用内存管理器,因此它属于该类别。这种用法很容易让您自己陷入困境。 - JarkkoL
1
@Simon Ninon:在这方面,“int”和“string”是灾难性不同的。 int支持静态初始化,而string需要动态初始化。通过用“int”替换“string”,您完全改变了示例的语义。不要替换,请发布真实代码。 - AnT stands with Russia
显示剩余5条评论
2个回答

4
感谢评论,我最终通过使用“constexpr”关键字解决了问题。
下面是可用的代码: file1.hpp
class A {
  public:
    static constexpr char MY_CONST_VAR[] = "some string";
};

file1.cpp

const char A::MY_CONST_VAR[];

file2.cpp

static std::string arrayOfString[] = {
  A::MY_CONST_VAR,
  ...
};

1
但是constexpr只在C++11中引入。祝你在可移植性方面好运。 - Kaz
这是在C/C++学校项目的背景下,兼容性并不重要。否则,我会像评论中提到的那样,在静态变量周围做一些包装。 - Simon Ninon
1
不不,继续使用它,这样每个人都可以提高编译器的水平 =) - oblitum
8
@Kaz说:"祝你在可移植性方面好运"。如果在2014年我们不认为C++11是“可移植”的,继续使用C'96编码……那么它永远不会成为现在的样子。 - Emilio Garavaglia
不,实际上解决您问题的是将需要动态初始化的类型std::string更改为不需要动态初始化的类型const char[]。在技术上可能添加expr更正确,但我99%确定它与(不)对静态对象进行动态初始化的问题没有任何关系或影响。 - underscore_d

3
一个通用的解决静态初始化失败问题的方法是将静态变量放在函数中,因为函数内的变量在函数被调用之前其初始值并不会被计算。但当数组的长度由初始值的数量确定时,这个方法就不那么直截了当了。然而,在我的看法中,访问一个全局 C 数组是一种糟糕的设计:你要么必须在每次使用数组时都污染你的代码以进行范围检查,要么就有可能进行越界访问。而且,数组边界错误是最难调试的错误之一。我个人会将代码替换为:
std::string &lookup_string(size_t n)
{
    static std::string arrayOfString[] = { A::MY_CONST_VAR(), .... };

    if ( n >= dimof(arrayOfString) ) throw....
    return arrayOfString[n];
}

如果在另一个文件中constexpr不可用:

std::string MY_CONST_VAR() { return "some string"; }

现在没有静态灾难了。
注:我假设您想要对arrayOfString进行写访问,因为您没有将其声明为const。如果它们应该是只读的,则可以进行进一步的改进。

是的,对于“通用解决方案”部分,我同意。但说实话,我觉得常量(尤其是大写)后面加括号看起来有点不好看,你真的喜欢这样吗? - Wolf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接