为什么创建静态const std :: string会导致异常?

Kai*_*zay 25 c++ static constant-expression

我有字符串常量,我在我的应用程序中的多个位置使用的字符串:

namespace Common{
    static const std::string mystring = "IamAwesum";
}
Run Code Online (Sandbox Code Playgroud)

当发布关于其他内容的问题时(编译期间未包含在目标中的.h文件会发生什么?),另一位用户发表了以下评论:

请注意,在这种情况下,您的静态字符串是全局的.所以他们可以在任何时候创建一个例外,并且无法捕获.我建议你使用返回字符串引用的函数.std :: string const&mystring {static std :: string const mystring ="IamAwesum"; 通过这种方式返回mystring}你的对象只在需要时构造

有人可以解释为什么以我上面这样的方式使用静态const字符串,冒着抛出异常的风险吗?

krz*_*zaq 31

N4140§3.6.2[basic.start.init]/4

它是实现定义的,是否在第一个语句之前完成了具有静态存储持续时间的非局部变量的动态初始化main.

N4140§N414015.3 [except.handle]/13

与静态存储对象的析构函数或与静态存储时间命名空间范围对象的构造函数抛出的异常没有被捕获功能试块main().

你根本无法捕获字符串构造函数生成的异常 - 比如说std::bad_alloc.

(意见)话虽如此,对于这么小的字符串,我觉得这种考虑是偏执的.

  • 另外,@ krzaq即使发生这种情况,我也不知道其缺点是什么.它不像捕捉一个'bad_alloc`就可以让你做一些你不能捕捉它时无法做到的东西,因为你现在无法保证任何东西. (8认同)
  • @John,基本上有两种模型,其中初始化代码由编译器插入到程序入口点.(1)编译器将它们注入到你的`main`函数定义中(2)编译器将你的`main`函数包装在另一个函数中,在那里进行初始化,然后你的`main`被调用.在第二种情况下,您无法在main函数中捕获异常,因为代码在其上方执行堆栈框架,可以这么说. (6认同)
  • 问题是在任何代码可以"运行"之前,运行时可以初始化静态.这意味着,如果你的字符串足够长并且你的内存足够小,你可能会遇到`bad_alloc`而你自己的`main`中没有运行一行代码,所以没有办法捕获这些异常. (2认同)
  • @John阅读了意见部分.除非您正在为嵌入式开发,否则我发现您不太可能遇到这样的问题. (2认同)
  • 当然,在某些嵌入式系统中,所有代码都已经在ROM中,有大量的进程和线程,其堆栈是预先分配的,可修改的静态数据区域的大小限制,所有这些,这可能意味着程序启动保证,而且`std :: terminate()`的后果可能比通过捕获异常所能实现的更糟.那么你肯定要确保捕获异常.如果你正在编写一个Windows应用程序,那么在10字节分配失败的时候系统就已经搞砸了,你所做的一切都没有帮助...... (2认同)

Dam*_*mon 5

唯一的“问题”——如果你可以这样称呼它——我在你的代码中看到的是,你通过不必要地将已经恒定的数据复制到动态分配的缓冲区(形式上是恒定的,但实际上不是恒定的)来浪费资源)。这会使用所需物理内存的两倍,并执行不必要的复制。

有关系吗?几乎可以肯定,不会。即使在“内存相当有限”的系统上,无论是从执行时间的角度还是从内存消耗的角度来看,这一点现在都很难被注意到。

至于异常,从技术上来说当然是正确的,std::string必须进行的分配可能会失败,因此构造函数可能会抛出异常,而您将无法捕获它。但请现实一点。
这几乎可以保证不会发生,但即使发生了……如果在程序启动时为几个字符串分配内存这样的小事失败了,那么您就会遇到一个完全不同规模的非常非常严重的问题!
此外,正如上面另一个答案的评论中指出的:假设这种情况确实发生,你打算怎么做?该程序完全无法运行,因此您可以想象杀死该程序。

现在,随着 C++17 的出现并不遥远,并且string_view已经在几个主流编译器中可用std::experimental,您可以尝试另一件事:使用正确的东西

string_view与 a 相反,Astring不会分配非常量内存,而是将常量数据复制到其中,然后假装它是常量。相反,它将直接管理指向常量数据的指针,仅此而已。
这样,您的常量就真正(不仅仅是形式上)常量,没有分配,不可能出现异常,并且不会使用双倍内存。在大多数情况下,它看起来和闻起来仍然像string. 唯一显着的区别是 astring_view不保证 nul 终止(但它指向的字符常量确实如此,所以这是无关紧要的),而且它确实是常量,不可修改......这正是您想要的。