More Effective C++ 读书笔记四——异常

条款9:利用destructors避免泄漏资源

这里开始介绍了auto_ptr,其实就是利用了c++局部对象在离开作用域的时候,其析构函数会被调用,来避免资源泄漏。这样的好处,就是不管是作用域正常结束(跑出代码块)还是异常结束(抛出异常),对象的析构函数都能保证被调用。

条款10:在constructors内阻止资源泄漏

c++只会析构已构造完成的对象。对象只有在其constructor执行完毕才算是完全构造妥当。

也就是说,c++不自动清理那些“构造期间抛出exceptions”的对象。所以你必须设计你的constructors,使它们在那种情况下亦能自我清理。

结论是:如果你以auto_ptr对象来取代pointer class members,你便对你的constructors做了强化工事,免除了“exceptions出现时发生资源泄漏”的危机,不再需要在destructors内亲自动手释放资源,并允许const member pointers得以和non-const member pointers有着一样优雅的处理方式

条款11:禁止异常流出destructors之外

析构函数的调用方式又两种情况:一种是当对象在正常情况下被销毁(离开了生命周期或明确的被删除);另一种是excpetion传播过程中的栈展开(stack-unwinding)机制。

如果控制权基于exception的因素离开dstructor,而此时正有另一个exception处于作用状态,c++会调用terminate函数

示例:
[cce lang=”cpp”]
#include <iostream>

class A
{
public:
A() {}
~A() {}
};

int main()
{
try{
A a;
throw 0;
} catch (int e) {
std::cout << “catch exception: ” << e << std::endl;
}

return 0;
}
[/cce]
这里A的析构函数没有抛出异常,执行结果是控制台输出了:catch exception: 0
[cce lang=”cpp”]
#include <iostream>

class A
{
public:
A() {}
~A() {throw 1;}
};

int main()
{
try{
A a;
throw 0;
} catch (int e) {
std::cout << “catch exception: ” << e << std::endl;
}

return 0;
}
[/cce]
这个例子里,A的析构函数抛出了一个异常,执行结果是悲剧的终止了:
terminate called after throwing an instance of ‘int’
已放弃
因此,在无法判断析构函数是那种情况被调用的时候,千万不要抛出异常,否则程序会死的很惨。
判断当前是否在一个未被捕获的异常传播过程中,可以使用exception头文件中的uncaught_exception函数。
同样两个小例子:
[cce lang=”cpp”]
#include <iostream>
#include <exception>

class A
{
public:
A() {}
~A() {
if(std::uncaught_exception()) {
std::cerr<< “have exception, cannot throw\n”;
} else {
std::cout<< “have no exception, can throw\n”;
throw 1;
}
}
};

int main()
{
try{
A a;
} catch (int e) {
std::cout << “catch exception: ” << e << std::endl;
}

return 0;
}
[/cce]
这里调用过程中没有抛出异常,也就是说,A的析构函数是在实例a离开try代码块的时候调用的,uncaught_exception函数将会返回false,执行结果为:
have no exception, can throw
catch exception: 1
[cce lang=”cpp”]
#include <iostream>
#include <exception>

class A
{
public:
A() {}
~A() {
if(std::uncaught_exception()) {
std::cerr<< “have exception, cannot throw\n”;
} else {
std::cout<< “have no exception, can throw\n”;
throw 1;
}
}
};

int main()
{
try{
A a;
throw 0;
} catch (int e) {
std::cout << “catch exception: ” << e << std::endl;
}

return 0;
}
[/cce]
这里a的析构是因为在栈展开过程中,uncaught_exception会放回true,这个时候应该阻止析构函数继续抛出异常,否则c++会调用terminate函数结束程序。这里执行结果是:
have exception, cannot throw
catch exception: 0
当然这样写代码太痛苦了,所以直接避免析构函数抛出异常最好。
另一个系统函数不能抛出异常的原因,是异常抛出了之后,析构函数后面的代码将不会执行,可能会导致还有部分资源没有释放。

转载自:https://coolex.info/blog/261.html

时间: 2024-10-31 15:39:25

More Effective C++ 读书笔记四——异常的相关文章

More Effective C++ 读书笔记五——异常

条款12:了解"抛出一个exception"与"传递一个参数"或"调用一个虚函数"之间的差异 第一,exception object总是会被复制,如果以by value方式捕捉,它们甚至被复制两次.至于传递给函数参数的对象不一定得复制.第二,"被抛出成为exceptions"的对象,其被允许的类型转换动作,比"被传递到函数去"的对象少.第三,catch子句以其"出现于源代码的顺序"被编译

《编程之美》读书笔记(四): 卖书折扣问题的贪心解法

  <编程之美>读书笔记(四):卖书折扣问题的贪心解法        每次看完<编程之美>中的问题,想要亲自演算一下或深入思考的时候,都觉得时间过得很快,动辄一两个小时,如果再把代码敲一遍的话,需要的时间可能更长,真是搞不懂通过微软面试的那些家伙的脑袋到底什么构造,书的序言中提到他们每次面试45分钟,还要写出程序?!在我看来,如果是控制CPU曲线或是中国象棋问题或许还有可能,如果是买书折扣问题,我觉得真的是不太容易,尤其是如果当面试者钻进本题的贪心解法而不是动态规划算法的思路之后,

Programming Ruby读书笔记(四)

Ruby正则表达式 三种表示方法: Regexp.new('^s*[a-z]') /^s*[a-z]/ %r...{^s*[a-z]} 测试代码: def show_reqexp(a, re) if a =~ re "#{$`} << #{$&} >> #{$'}" else "no match" end end puts show_reqexp("Fats Waller", /ll/) 结果:Fats Wa &l

《淘宝技术这十年》读书笔记 (四). 分布式时代和中间件

        前面两篇文章介绍了淘宝的发展历程.Java时代的变迁和淘宝开始创新技术:            <淘宝技术这十年>读书笔记 (一).淘宝网技术简介及来源            <淘宝技术这十年>读书笔记 (二).Java时代的脱胎换骨和坚若磐石            <淘宝技术这十年>读书笔记 (三).创造技术TFS和Tair        这篇文章主要讲述分布式时代和中间件相关知识,包括服务化.HSF.Notify和TDDL.同时里面有我们经常遇见的编

Effective C++ 读书笔记之Part1.Accustoming Yourself to C++

1.View C++ as a federation of languages C++的四个次语言: 1)C 2)Object-Oriented C++ 3)Template C++ 4)STL 2.Prefer consts, enums, and inlines to #defines 一方面是因为使用宏定义不利于调试的时候定位错误,另一方面主要是因为预处理器和编译器两者分工不同所导致的. 同时,宏定义太复杂的情况下很容易出现错误. 总结: 第一,对于单纯常量,最好以const对象或enum

Effective C++ 读书笔记之Part6.Inheritance and Object-Oriented Design

32.Make sure public inheritance models "is-a". 所谓的最佳设计,取决于系统希望做什么事,包括现在与未来. 需要解决的问题:其中关于两个assert都通过的地方有些疑惑. 总结: "public继承"意味着is-a.适用于base classes身上的每一件事情一定也适用于derived classes身上,因为每一个derived class对象也都是一个base class对象. 33.Avoid hiding inh

Effective C++ 读书笔记之Part2.Constructors, Destructors, and Assignment Operators

5.Know what functions C++ silently writes and calls. 总结:编译器可以暗自为class创建default构造函数.copy构造函数.copy assginment操作符,以及析构函数.这些函数都是public的,并且是inline的. 6.Explicitly disallow the use of compiler-generated functions you to not want. 总结:为驳回编译器自动(暗自)提供的机能,可将相应的成

Effective C++ 读书笔记之Part5.Implementations

 26. Postpone variable definitions as long as possible. 总结: 尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率. 批注: 纯C语言此处有冲突,C语言要求变量定义出现在代码快的开始部分. 27. Minimize casting. 1)const_cast 通常被用来将对象的常量性转除(cast away the constness) .它也是唯一有此能力的 C++-style 转型操作符. 2)dynamic_cast主

Effective C++ 读书笔记之Part4.Design and Declarations

18. Make interfaces easy to use correctly and hard to use incorrrectly. 总结: 第一,好的接口很容易被正确使用,不容易被误用.你应该在你的所有接口中努力达成这些性质. 第二,"促使正确使用"的办法包括接口的一致性,以及与内置类型的行为兼容. 第三,"阻止误用"的办法包括建立新类型.限制类型上的操作,束缚对象值,以及消除客户的资源管理责任. 第四,tr1::shared_ptr支持定制型删除器(c