1.几种与指针相关的const
-
const char* ptr = data:指针不能变换,指针的值可以变换
-
char* const ptr = data:指针可以变换,指针的值不可以变换
-
const char* const ptr = data:指针不能变换,指针的值也不能变换
2.抛出异常
- throw()
class MyError {
const char* const message;
public:
MyError(const char* const msg = 0) : message(msg) {
}
};
void f() {
//抛出一个y自定义的异常类
throw MyError("something bad happened");
}
int main() {
f();
}
terminating with uncaught exception of type MyError
Abort trap: 6
- try()…catch(Type e)捕获异常
当执行函数oz()中的throw语句时,程序的控制流开始回溯,直到找到catch(参数为抛出的异常类型的参数),进而在其子句中恢复运行。注意:当throw语句造成程序的执行过程从oz()函数时,函数f()里的所有对象的析构函数都会被调用。
class MyError {
const char* const message;
public:
MyError(const char* const msg = 0) : message(msg) {
}
string getMessage(){
return message;
}
};
void f() {
//抛出一个y自定义的异常类
throw MyError("something bad happened");
}
int main() {
try {
f();
} catch (MyError& e) {
cout << e.getMessage() << endl;
}
}
- 异常匹配
(1)如果有基类异常跟派生类异常,若catch(基类异常) 在前,它会捕获基类异常跟所有派生类异常,因此比较有意义的做法是,应先将catch(派生类类异常)放在前面。
(2)通过引用捕获异常,防止异常对象所包含的信息被切割掉。
class X {
public:
class Trouble {
};
class Big : public Trouble {
};
class Small : public Trouble {
};
void f() {
throw Big(); } //Big();调用构造函数,创建一个对象
};
int main() {
try {
X x;
x.f();
} catch (X::Trouble&) {
cout << "catch Trouble" << endl;
} catch (X::Big&) {
cout << "catch Big" << endl;
} catch (X::Small&) {
cout << "catch Small" << endl;
}
}
3.清理
异常抛出后,程序必须要做恰当的清理工作。C++的异常处理必须确保当程序的执行流程离开一个作用域的时候,对于这个作用域的所有由构造函数建立起来对象,它们的析构函数一定会被调用。
class Trace
{
public:
static int counter;
int objid;
public:
Trace() {
objid = counter++;
cout << "constructing Trace #" << objid << endl;
if(objid == 3) throw 3;
}
~Trace(){
cout << "deconstructing Trace #"<< objid << endl;
}
};
int Trace::counter = 0;
int main(int argc, char const *argv[])
{
try{
Trace n1;
//Throws exception
Trace array[5]; //当第四个对象(#3)被完整创建之前抛出了异常,因此此前所有由构造函数构建的对象,其析构函数都会被调用
Trace n2; //Won't get here
}catch(int i){
cout << "caught" << endl;
}
return 0;
}
constructing Trace #0
constructing Trace #1
constructing Trace #2
constructing Trace #3
deconstructing Trace #2
deconstructing Trace #1
deconstructing Trace r#0
caught
4."悬挂"指针(“naked pointer”)
如果在构造函数中发生异常,则不会调用作用域里的所有析构函数。
class Cat
{
public:
Cat( ) {
cout << "Cat()" <<endl; }
~Cat() {
cout << "~Cat()" <<endl; }
};
class Dog
{
public:
Dog(){
}
~Dog(){
}
void* operator new(size_t sz) {
cout << "allocating a Dog" << endl;
throw 47;
}
void operator delete(void* p) {
cout << "deallocating a Dog" << endl;
//::operator(p);
}
};
class UseResources
{
Cat* bp;
Dog* op;
public:
UseResources(int count = 1){
cout << "UseResources()" << endl;
bp = new Cat[count]();
op = new Dog(); //在创建Dog对象的时候,由于内存不足,触发一个异常抛出
}
~UseResources(){
cout << "~UseResources()" << endl;
delete [] bp; // Array delete
delete op;
}
};
int main(int argc, char const *argv[])
{
try{
UseResources ur(3);
} catch(int) {
cout << "inside handler" << endl;
}
return 0;
}
如结果所示:UseResources对象与Cat对象均无调用析构函数
UseResources()
Cat()
Cat()
Cat()
allocating a Dog
inside handler
5.资源获得式初始化(Resource Acquisition Is Initialization)[RAII]
- 使用下述二者之一来防止“不成熟的”资源分配方式:
1)在构造函数中捕获异常,用于释放资源;
2)在对象的构造函数中分配资源,并且在对象的析构函数中释放资源。
由于资源分配成为局部对象生命周期的一部分,如果某次分配失败了,那么在栈反解的时候,其他已经获得所需资源的对象能够被恰当地清理。这种技术称为资源获得式初始化,因为它使得对象对资源控制的时间与对象的生命周期相等。
template<class T, int sz = 1>
class PWrap
{
T* ptr;
public:
class RangeError {
}; //Exception class
PWrap() {
ptr = new T[sz]; //用指针指向new分配的地址
cout << "PWrap constructor..." << endl;
}
~PWrap(){
delete[] ptr; //关键字delete[],释放数组资源
cout << "PWrap deconstructor..." << endl;
}
T& operator[](int i) throw(RangeError) {
if (i >= 0 && i < sz) return ptr[i];
throw RangeError();
}
};
class Cat
{
public:
Cat() {
cout << "Cat()" << endl; }
~Cat() {
cout << "~Cat()" << endl; }
void g() {
}
};
class Dog
{
public:
//重写new[]函数
void* operator new[] (size_t) {
cout << "Allocating a Dog" << endl;
throw 47;
}
void operator delete[](void *p){
cout << "Deallocating a Dog" << endl;
::operator delete[](p);
}
};
class UseResources
{
PWrap<Cat, 3> cats;
PWrap<Dog> dog;
public:
UseResources() {
cout << "UseResources()" << endl; }
~UseResources(){
cout << "~UseResources()" << endl; }
void f() {
cats[1].g(); }
};
int main(int argc, char const *argv[])
{
try {
UseResources ur;
} catch(int) {
cout << "inside handler" << endl;
} catch(...) {
//catch(...) 捕获所有类型的
cout << "inside catch(...)" << endl;
}
return 0;
}
- //关键字delete[],释放数组资源
- //catch(…) 捕获所有类型的错误
- //void operator delete[](void *p) //重写delete,以后Dog对象调用的就是Dog::delete[] ()。而::operator delete调用的是C++里的delete[] ()
Cat()
Cat()
Cat()
PWrap constructor…
Allocating a Dog
~Cat()
~Cat()
~Cat()
PWrap deconstructor…
inside handler
6.什么时候避免异常
- (1)不要在异步事件中使用异常;
- (2)不要在处理简单错误的时候使用异常:
如果能得到足够的信息来处理错误,那么就不要使用异常。程序员应该在当前语境中处理这个错误,而不是将一个异常抛到更大的(上一层)语境中。 - 不要将异常用于程序的流程控制;
- 不要强迫自己使用异常:
最好把清理工作交给OS来处理,而不必费劲地捕获所有异常并释放资源。 - 新异常,老代码;