Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 异常安全-何时、如何、为什么?_C++_Exception_Exception Safe - Fatal编程技术网

C++ 异常安全-何时、如何、为什么?

C++ 异常安全-何时、如何、为什么?,c++,exception,exception-safe,C++,Exception,Exception Safe,我只是一个初出茅庐的程序员,至少会尝试编写比最佳场景更多的程序。我一直在阅读Herb Sutter的“exception C++”,到目前为止,我已经三次阅读了异常安全章节。然而,除了他提出的例子(一堆),我真的不确定什么时候我应该努力实现异常安全和速度,什么时候这样做是愚蠢的 例如,我当前的作业项目是一个双链接列表。由于我已经编写了一些程序,所以我想花点时间深入了解一些更深入的概念,比如ES 以下是我的弹出式前台功能: void List::pop_front() { if(!head

我只是一个初出茅庐的程序员,至少会尝试编写比最佳场景更多的程序。我一直在阅读Herb Sutter的“exception C++”,到目前为止,我已经三次阅读了异常安全章节。然而,除了他提出的例子(一堆),我真的不确定什么时候我应该努力实现异常安全和速度,什么时候这样做是愚蠢的

例如,我当前的作业项目是一个双链接列表。由于我已经编写了一些程序,所以我想花点时间深入了解一些更深入的概念,比如ES

以下是我的弹出式前台功能:

void List::pop_front()
{
    if(!head_)
        throw std::length_error("Pop front:  List is empty.\n");
    else
    {
        ListElem *temp = head_;
        head_          = head_->next;
        head_->prev    = 0;
        delete temp;
        --size_;
    }
}
void List::push_back(const T& data)
{
    if(!tail_)
    {
        tail_ = new ListElem(data, 0, 0);
        head_ = tail_;
    }
    else
    {
    tail_->next = new ListElem(data, 0, tail_);
    tail_ = tail_->next;
    }
    ++size_;
}
我在这方面遇到了一些难题

1) 当列表失败时,我真的应该抛出错误吗?我不是应该什么都不做并返回,而不是强制列表的用户执行try{]catch(){}语句(这也很慢)

2) 有多个错误类(加上我的老师要求我们在课堂上实现的ListException)。这样的事情真的需要自定义错误类吗?是否有关于何时使用特定异常类的一般指南?(例如,范围、长度和边界听起来都一样)

3) 我知道在抛出异常的所有代码都完成之前,我不应该更改程序状态。这就是为什么我要递减size\uuuLast。在这个简单的示例中,这真的是必要的吗?我知道delete不能抛出。当分配给0时,head->prev是否可能抛出?(head是第一个节点)

我的后推功能:

void List::pop_front()
{
    if(!head_)
        throw std::length_error("Pop front:  List is empty.\n");
    else
    {
        ListElem *temp = head_;
        head_          = head_->next;
        head_->prev    = 0;
        delete temp;
        --size_;
    }
}
void List::push_back(const T& data)
{
    if(!tail_)
    {
        tail_ = new ListElem(data, 0, 0);
        head_ = tail_;
    }
    else
    {
    tail_->next = new ListElem(data, 0, tail_);
    tail_ = tail_->next;
    }
    ++size_;
}
< P > 1)我经常听到C++程序中任何东西都会失败。测试ListelEm的构造函数是否失败(或者在代码< Trace>新< /COD> ING)时是现实的吗? 2) 是否有必要测试数据类型(目前是一个简单的
typedefint T T
,直到我将所有内容模板化),以确保该类型适用于该结构


我意识到这些都是过于简单的例子,但我目前只是对何时应该实践好的ES和何时不实践感到困惑。

这是一个很长的问题。我将回答所有编号为
1)
的问题

1) 当一个列表失败时,我真的应该抛出一个错误吗?我不是应该什么都不做并返回,而不是强迫列表的用户执行try{]catch(){}语句(这也很慢)

否。如果用户关心性能,他们会在尝试弹出前检查长度,而不是弹出并捕获异常。如果用户忘记先检查长度,则会出现异常。此时,你真的希望应用程序在他们面前爆炸。如果你什么都不做,可能会导致微妙的后果问题只会在以后出现,这将使调试更加困难

1)我经常听说C++程序中任何东西都会失败。测试ListelEm的构造函数失败(或者在新的时候进行TraceIn)是现实的吗? 例如,如果内存不足,构造函数可能会失败,但在这种情况下,它应该引发异常,而不是返回null。因此,您不需要显式测试构造函数是否失败。有关更多详细信息,请参阅此问题:

<>我经常听到任何一个C++程序都会失败。测试ListelEm的构造函数失败(或者在新的时候进行TraceIn)是现实的吗? 是的,这是现实的。否则,如果你的程序内存不足,分配失败(或者构造函数由于其他内部原因失败),你以后就会遇到问题

基本上,当代码无法完全执行API声明的操作时,必须发出失败信号


唯一的区别是如何通过返回值或异常来表示故障。如果存在性能方面的考虑,返回值可能比异常更好。但这两种方法都需要调用方中的特殊错误捕获代码。

对于第一组问题:

  • 是的,你应该投,因为@Mark回答中的所有原因。(+1对他)
  • 这并不是真的必要,但它可以让调用方的生活变得更加轻松。异常处理的好处之一是,它将代码本地化,以便在一个位置一起处理特定的错误类别。通过抛出特定的异常类型,您可以让调用方专门捕获该错误,或者通过捕获更一般的错误您抛出的特定异常的超类
  • else
    中的所有语句都提供了nothrow保证
  • 第二套:

  • 不,测试是不现实的。你根本不知道底层构造函数会抛出什么。它可能是一个预期项(即
    std::bad_alloc
    ),也可能是一些奇怪的东西(即
    int
    ),因此处理它的唯一方法是将它放在
    catch(…)
    中,这是邪恶的:)

    另一方面,现有方法已经是异常安全的,只要在
    if
    块中创建的虚拟结束节点将被链表的析构函数核化。(即
    s之后的所有内容都不提供任何行)

  • 只要假设
    T
    上的任何操作都可以抛出,析构函数除外

  • 当一个列表失败时,我真的应该抛出一个错误吗?我不是应该什么都不做并返回,而不是强迫列表的用户执行try{]catch(){}语句(这也很慢)

    绝对抛出异常

    如果列表为空,则用户必须知道发生了什么-否则将无法调试。如果异常是意外的(即,只能由于程序员错误而发生),则用户不会被迫使用try/catch语句,那么就没有理由尝试捕获它。当异常未捕获时,它将转到std::terminate和这是非常有用的行为