Think in in C++[vol2]【1】异常处理

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来处理,而不必费劲地捕获所有异常并释放资源。
  • 新异常,老代码;

猜你喜欢

转载自blog.csdn.net/qq_43118572/article/details/112993160
今日推荐