C++ 异常安全-何时、如何、为什么?
我只是一个初出茅庐的程序员,至少会尝试编写比最佳场景更多的程序。我一直在阅读Herb Sutter的“exception C++”,到目前为止,我已经三次阅读了异常安全章节。然而,除了他提出的例子(一堆),我真的不确定什么时候我应该努力实现异常安全和速度,什么时候这样做是愚蠢的 例如,我当前的作业项目是一个双链接列表。由于我已经编写了一些程序,所以我想花点时间深入了解一些更深入的概念,比如ES 以下是我的弹出式前台功能:C++ 异常安全-何时、如何、为什么?,c++,exception,exception-safe,C++,Exception,Exception Safe,我只是一个初出茅庐的程序员,至少会尝试编写比最佳场景更多的程序。我一直在阅读Herb Sutter的“exception C++”,到目前为止,我已经三次阅读了异常安全章节。然而,除了他提出的例子(一堆),我真的不确定什么时候我应该努力实现异常安全和速度,什么时候这样做是愚蠢的 例如,我当前的作业项目是一个双链接列表。由于我已经编写了一些程序,所以我想花点时间深入了解一些更深入的概念,比如ES 以下是我的弹出式前台功能: void List::pop_front() { if(!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_;
}
我在这方面遇到了一些难题
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。因此,您不需要显式测试构造函数是否失败。有关更多详细信息,请参阅此问题:
唯一的区别是如何通过返回值或异常来表示故障。如果存在性能方面的考虑,返回值可能比异常更好。但这两种方法都需要调用方中的特殊错误捕获代码。对于第一组问题:
else
中的所有语句都提供了nothrow保证std::bad_alloc
),也可能是一些奇怪的东西(即int
),因此处理它的唯一方法是将它放在catch(…)
中,这是邪恶的:)
另一方面,现有方法已经是异常安全的,只要在if
块中创建的虚拟结束节点将被链表的析构函数核化。(即新s之后的所有内容都不提供任何行)
T
上的任何操作都可以抛出,析构函数除外