我只是一个初出茅庐的程序员,至少尝试编程而不是最好的情况.我一直在阅读Herb Sutter的"Exceptional C++",并且到目前为止已经完成了三次异常安全章节.然而,除了他提出的一个例子(一个堆栈),我不确定我应该在什么时候努力争取异常安全与速度以及何时这样做是非常愚蠢的.
例如,我目前的家庭作业项目是一个双重链接列表.由于我已经编写了其中的几个,我想花时间深入研究一些更深层次的概念,比如ES.
这是我的pop-front功能:
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_;
}
}
Run Code Online (Sandbox Code Playgroud)
我有一些困境.
1)当列表失败时,我真的应该抛出错误吗?我不应该简单地做什么并返回而不是强迫列表的用户执行try {] catch(){}语句(这也很慢).
2)有多个错误类(加上我的老师要求我们在类中实现的ListException).自定义错误类对于这样的事情是否真的是必需的,并且是否有关于何时使用特定异常类的一般指导?(例如,范围,长度和边界都相似)
3)我知道我不应该更改程序状态,直到抛出异常的所有代码都完成.这就是我最后减小size_的原因.这个简单的例子真的有必要吗?我知道删除不能扔.分配给0时头_-> prev是否可能抛出?(头是第一个节点)
我的push_back函数:
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_;
}
Run Code Online (Sandbox Code Playgroud)
1)我经常听到C++程序中的任何事情都会失败.测试ListElem的构造函数是否失败(或者在执行期间是tail_ new)是否现实?
2)是否有必要测试数据类型(目前很简单,typedef int T …
每当我需要将动态分配的对象添加到向量中时,我一直在做以下方式:
class Foo { ... };
vector<Foo*> v;
v.push_back(new Foo);
// do stuff with Foo in v
// delete all Foo in v
Run Code Online (Sandbox Code Playgroud)
它只是工作,许多其他人似乎做同样的事情.
今天,我学会了vector :: push_back可以抛出异常.这意味着上面的代码不是异常安全的.:-(所以我提出了一个解决方案:
class Foo { ... };
vector<Foo*> v;
auto_ptr<Foo> p(new Foo);
v.push_back(p.get());
p.release();
// do stuff with Foo in v
// delete all Foo in v
Run Code Online (Sandbox Code Playgroud)
但问题是新的方式冗长乏味,我看到没有人这样做.(至少不在我身边......)
我应该采用新的方式吗?
或者,我可以坚持旧的方式吗?
或者,有更好的方法吗?
我想知道是否synchronized异常安全?比如,在synchronized块中发生未捕获的异常,是否会释放锁定?
在下面的代码中,作者指出new operator函数调用可能会导致异常,因此此实现不是异常安全的,因为对象状态已在第一行中更改。
String &String::operator =( const char *str ) {
// state is changed
delete [] s_;
if( !str ) str = "";
// exception might occur because of new operator
s_ = strcpy( new char[ strlen(str)+1 ], str );
return *this;
}
Run Code Online (Sandbox Code Playgroud)
在阅读时,我想知道 C 库函数会在 C++ 中抛出异常吗?我知道 C 中没有例外,但由于我们使用的是 C++ 编译器,因此可能会有例外。
那么,我们可以将 C 标准库函数视为异常安全的函数调用吗?
谢谢你。
顺便说一句,为了记录,实现上述功能的正确方法(异常安全)如下。
String &String::operator =( const char *str ) {
if( !str ) str = "";
char *tmp = strcpy( new char[ strlen(str)+1 …Run Code Online (Sandbox Code Playgroud) c++ exception exception-safe exception-safety c-standard-library
从std::unique_ptr<>()的文档中,我不清楚初始化指针时可能会发生什么。
分配时std::shared_ptr<>(),它会分配一个内存缓冲区来处理引用计数器。所以我可能会得到一个std::bad_alloc例外。
初始化唯一指针时会发生类似的事情吗?
我问这个问题是因为如果确实如此,我实际上可能会丢失我试图通过唯一指针删除的内容。例如:
void deleter(FILE * f)
{
fclose(f);
}
void func()
{
...
FILE * f(fopen("/tmp/random", O_CREAT | ...));
if(f == nullptr) ...handle error...
std::unique_ptr<FILE, decltype(&deleter)> raii_file(f, deleter);
...
}
Run Code Online (Sandbox Code Playgroud)
所以,如果初始化unique_ptr<>()可以抛出,我最终可能会f永远保持文件打开。(我FILE *举个例子,任何类似的资源都可能受到影响。)
反对这个答案,我显然不能使用,std::make_unique<>()因为我不只是分配内存。
初始化std::unique_ptr<>()之前的fopen(),然后将值保存在之后会更安全吗?
...
std::unique_ptr<FILE, decltype(&deleter)> raii_file(nullptr, deleter);
FILE * f(fopen("/tmp/random", O_CREAT | ...));
if(f == nullptr) ...handle error...
raii_file = f;
...
Run Code Online (Sandbox Code Playgroud)
或者会不会有类似的问题?
假设你有一段代码如下:
resource = allocateResource();
try { /* dangerous code here */ }
finally { free(resource); }
Run Code Online (Sandbox Code Playgroud)
我在这里没有提到任何特定的语言,但我猜Java,C#和C++就是很好的例子(假设你在MSVC++中使用__try/ __finally).
就个人而言,我不认为这是异常安全的,因为如果在进入try块之前有异常怎么办?那你的资源就会泄漏.
不过,我已经看过很多次了,我觉得我错过了什么......是吗?或者这真的不安全吗?
我不是要求allocateResource抛出异常,而是在该函数返回之后但在 resource分配之前得到异常的情况.