繁体   English   中英

C ++ 11引入了使用`const char *`的异常构造函数。 但为什么?

[英]C++11 introduced exception constructors taking `const char*`. But why?

标准库缺陷#254 ,它涵盖了新的异常构造函数的添加:

std::logic_error::logic_error(const char* what_arg);
std::runtime_error::runtime_error(const char* what_arg);
// etc.

作为基本原理,存储std::string s的想法打开了一些与可能存在问题的内存分配有关的蠕虫。

然而, 在休息室中orlp开始讨论之后,我觉得除非标准要求what_arg只是一个字符串文字(或指向静态存储持续时间的其他缓冲区的指针),否则它必须执行反正是C-string的副本,以保持成员函数的良好定义what()

那是因为:

void bar() {
   char buf[] = "lol";
   throw std::runtime_error(buf);
}

void foo() {
   try {
      bar();
   }
   catch (std::exception& e) {
      std::cout << e.what() << '\n';   // e.what() points to destroyed data!
   }
}

但我看不到任何这样的任务。 实际上,异常对象是否深层复制what_arg似乎完全没有指定。

如果他们这样做 ,那么首先添加重载(消除额外分配)的大部分理由似乎完全是空的。

这可能是标准缺陷,还是我在这里遗漏了什么?
这只是“程序员:不要在任何地方传递悬空指针”的情况吗?

这允许(或者至少显然是为了方便 - 在下面看到更多)实现,以消除副本,如果它可以检测到(通过本身不是标准化的方式)传递的是字符串文字或静态的其他内容储存期限。

例如,假设编译器将所有字符串文字汇集到一个由__string_literals_begin__string_literals_end分隔的范围内。 然后在std::exception的构造函数中的某个地方,它可以具有以下常规顺序的代码:

namespace std {
    exception::exception(char const *s) { 
        if (in_range(s, __string_literals_begin, __string_literals_end)) {
            stored_what = s;
            destroy_stored_what = false;
        }
        else {
            stored_what = dupe(s);
            destroy_stored_what = true;
        }
        // ...
    }

    exception::~exception() {
        if (destroy_stored_what)
            delete_string(stored_what);
}

链接DR中的最终评论指出:

[Oxford:提议的解决方案只是解决了使用const char *和字符串文字构造异常对象的问题,而无需显式包含或构造std :: string。 ]

因此,根据当时的评论,委员会意识到这些超载并未满足所有需求,但确实解决了(至少被认为是)需求。

(几乎)确实如果实施可以提供这些超载,即使它们没有被标准规定 - 尽管如此,委员会似乎已经确信添加它们对于上述情况主要(如果不是唯一的话)是有用的 - - 当字符串文字传递给ctor时,只有一个浅拷贝。

我不确定它是否是原因,但有一点是runtime_error保证它的复制构造函数不会抛出,这表明某种引用计数机制。

额外的构造函数意味着它只需要制作一个副本,从char *到底层mechinsm,而不是两次。 一旦进入一个字符串然后进入参考机制

缺陷的解决方案解决了另一个问题。 缺陷的底部有这样的说明:

[Oxford:提议的解决方案只是解决了使用const char *和字符串文字构造异常对象的问题,而无需显式包含或构造std :: string。 ]

libc ++如何处理这个问题?

他们目前使用ref-counting字符串来存储消息:

class _LIBCPP_EXCEPTION_ABI logic_error
    : public exception
{
private:
    _VSTD::__libcpp_refstring __imp_;

其中__imp_初始化如下:

logic_error::logic_error(const string& msg) : __imp_(msg.c_str()) {}

logic_error::logic_error(const char* msg) : __imp_(msg) {}

这个字符串__libcpp_refstring在存储新的char const*时会分配一个新的缓冲区:

explicit __libcpp_refstring(const char* msg) {
        std::size_t len = strlen(msg);
        _Rep_base* rep =
             static_cast<_Rep_base *>(::operator new(sizeof(*rep) + len + 1));

但是,当然, __libcpp_refstring的复制构造函数不会分配新的缓冲区。

(是的,这不能回答这个问题,但它应该对我认为的问题有所了解。例如,如果在logic_error中只有一个std::string const& ctor,则必须为ref添加一个额外的分配-计数器。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM