【问题标题】:Is it safe to use cerr when handling bad_alloc?处理 bad_alloc 时使用 cerr 是否安全?
【发布时间】:2015-12-18 10:16:05
【问题描述】:

这样使用std::cerr安全吗?

try 
{
      Something();
} 
catch(std::bad_alloc) 
{
    cerr << "Out of memory!";
}

它是否使用动态内存?如果失败,会抛出异常还是不输出任何东西?

【问题讨论】:

  • 我认为cerr 在程序启动时初始化后不太可能从堆中获取内存。我不确定有什么可以保证的。它的初始化是有保证的,这也是通常获取堆资源的地方。
  • 我不知道标准中的任何一行保证cerr &lt;&lt; 永远不会尝试从堆中分配内存,但它保证会进行无缓冲输出并刷新某些其他流,所以它是给定的实现不太可能。测试一个循环,一次吞噬一个字节,看看你的循环是否如此?
  • 我刚刚发现(根据标准),即使在初始化之后,cerr 在第一次调用std::basic_ios::copyfmt 时也会为数组分配内存。我想在程序开始时调用它可能会让你更安全......?
  • Gah 根据en.cppreference.com/w/cpp/io/ios_base/iword,该数组似乎可以随时重新分配。我认为所有的赌注都没有了。当灾难袭来时,它就是一场灾难。

标签: c++ exception-handling


【解决方案1】:

简单案例

有一个大分配失败-可能是由于程序员的错误-

int main() 
{

    try {
        std::size_t bytesToAllocate;
        std::cin >> bytesToAllocate;

        std::unique_ptr<char> ptr { new char[bytesToAllocate - 1] };
        // ops, if user enters 0 or extraction fails we get 
        // std::numeric_limits<std::size_t>::max() -1 bytes to allocate

    } catch (const std::bad_alloc& e) {
            std::cerr << "Failed to allocate memory.\n";
    }
}

在这里,即使new 失败,我们肯定还有更多内存,因为之前没有使用过。

现实案例

如果由于某种未指定的原因,字符插入失败,则启用内部failbit,即setstate(std::ios_base::failbit),如果exception is set 用于failbit,则会引发异常。此外,如果在插入过程中抛出异常,则设置badbit,如果在badbit 上设置exception,则重新抛出异常。

但是,AFAIK,它没有被覆盖,因此未指定此类操作是否分配内存以及它是如何完成的。您的程序可能会因为内存不足保护而被终止,因此如果在这种情况下整个异常传播过程完全可能的话,您的程序可能会被杀死,而没有机会捕获异常。

【讨论】:

  • 所以我理解正确,std::cerr 是不安全的,因为它可以抛出一个新的异常?将 cerr 包裹在第二次 try catch 中,这无济于事吗?目的是为了诊断,所以如果它不能输出是可以容忍的。但是,cerr 不应使问题复杂化。
  • cerr 和其他 I/O 对象如果你告诉它们就会抛出。正如答案中所说,似乎没有指定在OOM的情况下会发生什么,如果cerr使用内存以及如何使用。
  • @gist 你会让这个问题再开放一段时间吗(然后留下已解决的标记)?有人可以提出更好的答案,也许有具体的经验。
【解决方案2】:

standard(27.7.3.6, page 1057) 定义了 ostreams 中格式化输出函数的一些要求:

每个格式化的输出函数通过构造一个哨兵类的对象开始执行。如果此对象在转换为 bool 类型的值时返回 true,则该函数将努力生成请求的输出。如果生成失败,则格式化输出函数会执行 setstate(ios_base::failbit) ,可能会引发异常。如果输出时抛出异常,则 ios::badbit 在 *this 的错误状态下开启 328。如果 (exceptions()&badbit) != 0 则重新抛出异常。 无论是否抛出异常,在离开格式化输出函数之前,哨兵对象都会被销毁。如果没有抛出异常,则格式化输出函数的结果是 *this 。

(强调我的)

对于哨兵对象的构造(与构造任何对象一样),程序将需要更多内存。它是静态内存还是动态内存未指定。此外,正如黑人的回答总结得很好,该标准定义了启用 failbit 时可能会引发异常。

【讨论】:

    猜你喜欢
    • 2020-02-22
    • 1970-01-01
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-25
    • 1970-01-01
    • 2011-05-04
    相关资源
    最近更新 更多