1.使用析构函数防止资源泄露
考虑以下:
void processAdoptions(istream& dataSource)
{
while(dataSource)
{
ALA *pa = readALA(dataSource);
pa->processAdoption();
delete pa;
}
}
如果pa->processAdoption()出现异常 运行不到 delete会出现资源泄露。
方法一: try catch包裹代码块,不过这种方式 重复代码多
方法二: 包装指针 (智能指针的实现) pointer-like 的对象在析构函数中释放对象。
思想:用一个对象存储需要被自动释放的资源,然后依靠对象的析构函数来释放资源。运用这种思想可以很好避免在存在异常环境里发生资源泄露。
2.在构造函数中防止资源泄露
同上,使用智能指针包装指针对象,防止在构造函数中的问题。
class BookEntry{
public:
...
private:
...
const auto_ptr<Image> theImage;
const auto_ptr<Audioclip> theAudioClip;
}
BookEntry::BookEntry(const string& name, const string& address, const string& imageFileName, const string& audioClipFileName)
:theName(name), theAddress(address), theImage(imageFileName != "" ?
new Image(imageFileName : 0),
theAudioClip (audioClipFileName != "" ? new AudioClip(audioCipFIleName) : 0))
{
}
3.禁止异常信息传递到析构函数外
Session::~Session()
{
try{
logDestruction(this);
}
castch(...){}
}
捕获所有异常,使之抛出的异常不会被传递到session析构函数外面。
作用:
1.能够在异常传递的堆栈辗转开解的过程中,防止terminate被调用
2.它能帮助确保析构函数总能完成我们希望它做的所有事情。
4.理解“抛出一个异常”与“传递一个参数”或“调用一个虚函数”之间的差异
throw 一个异常到catch里与函数传参的一些异同点。
1.它们都是通过传值,传引用或者传递指针。但是调用函数时,程序的控制权最终会返回函数的调用处,但抛出一个异常控制权永远不会返回抛出异常的地方。
2.c++规范要求被作为异常抛出的对象必须被复制并传递,所以抛出异常运行速度比参数传递慢。
3.当异常对象被拷贝时,拷贝操作是由对象的拷贝函数完成的,为对象的静态类型所对应类的拷贝构造函数。
4.异常抛出的对象可以通过普通的引用捕捉,它不需要通过指向const对象的引用捕获,在函数调用中不允许传递一个临时对象到一个非const引用类型的参数里。
5.catch(Widget w) 这样传递异常会建立两个被抛出对象的拷贝,一个是所有异常都必须建立的临时对象,第二个是把临时对象拷贝进w中(两个) 抛出的指针不能指向局部对象,必须是全局或者堆中的。
6.catch(var value) 传递的参数一般不会进行隐式转换,不过在catch子句进行异常匹配时可以进行两种类型转换,一种是继承类与基类间的转换,第二种允许从一个类型化指针转变成无类型指针,所以带有const void*指针的catch子句能捕获任何类型的指针类型异常。
7.catch子句进行异常类型匹配的顺序是它们在源代码中出现的顺序,第一个类型匹配成功的catach将被用来执行。
5.通过引用捕获异常
通过值或者引用捕捉(指针存在问题)。
传值会出现一个问题,与函数类似(当一个对象通过传值方式传递给函数,静态绑定(估计是拷贝时类型转换了,失去了多态性))
通过引用捕获异常避开上述问题,不像指针捕捉会因为对象删除的问题而且也能捕获标准异常类型。也不像通过值捕获异常,这种方法没有slicing problem,而且异常对象只被拷贝一次。
建议使用引用捕获异常。
6.审慎使用异常规格
c++ 11使用noexcept
1.避免在带有类型参数的模板内使用异常规格。
template<class T>
bool operator==(const T& lhs, const T& rhs) throw()
{
return &lhs == &rhs;
}
模板和异常规格不要混合使用。
2.如果在一个函数内调用其它没有异常规格的函数时应该去除这个函数的异常规格。
7.了解异常处理的系统开销