【ThinkingInC++】69、异常处理
第一章 异常处理
1.5清理
1.5.1 资源管理
如果一个对象的构造函数在执行过程中抛出异常,那么这个对象的析构函数就不会被调用。
Rawp.cpp
/** * 书本:【ThinkingInC++】 * 功能:资源管理 * 时间:2014年10月8日20:19:03 * 作者:cutter_point */ #include <iostream> #include <cstddef> using namespace std; class Cat { public: Cat() { cout<<"Cat()"<<endl; } ~Cat() { cout<<"~Cat()"<<endl; } }; class Dog { public: void* operator new(size_t sz) { cout<<"分配一个Dog的空间"<<endl; throw 47; //抛出异常,后面为了检验构造函数抛出异常的后果 } void operator delete(void* p) { cout<<"回收一个Dog的空间"<<endl; ::operator delete(p); } }; class UseResources { Cat* bp; Dog* op; //这个事类的组合 public: UseResources(int count=1) { cout<<"UseResource的构造函数"<<endl; bp=new Cat[count]; op=new Dog; } ~UseResources() { cout<<"~UseResources的析构函数"<<endl; delete [] bp; //回收数组空间 delete op; } }; int main() { try { UseResources ur(3); } catch(int) { cout<<"inside handler"<<endl; } return 0; }
1.5.2 使所有事物都成为对象
·在构造函数中捕获
·在对象的构造函数中分配资源,并且在对象的析构函数中释放资源。
Wrapped.cpp
/** * 书本:【ThinkingInC++】 * 功能:使所有事物都成为对象,防止资源泄露 * 时间:2014年10月8日20:19:38 * 作者:cutter_point */ #include <iostream> #include <cstddef> using namespace std; template<class T, int sz=1> class PWrap { T* ptr; //一个这个类型的指针 public: class RangeError {}; //这是一个异常类 PWrap() { ptr=new T[sz]; //构造函数,给数据成员创建初值 cout<<"PWrap的构造函数"<<endl; } ~PWrap() { delete [] ptr; //析构 cout<<"PWrap的析构函数"<<endl; } T& operator [] (int i) throw(RangeError) //抛出一个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: void* operator new[](size_t) { cout<<"分配狗的空间"<<endl; throw 47; //内存不够抛出异常 } void operator delete[](void* p) { cout<<"回收狗的内存空间"<<endl; ::operator delete[](p); } }; class UseResources { PWrap<Cat, 3> cats; //创建3只猫 PWrap<Dog> dog; public: UseResources() { cout<<"UseResources构造函数"<<endl; } ~UseResources() { cout<<"~UseResources析构函数"<<endl; } void f() { cats[1].g(); } }; int main() { try { UseResources ur; } catch(int) { cout<<"抛出异常,捕获一个整形的数值"<<endl; } catch(...) { cout<<"其他的捕获(...)"<<endl; } return 0; }
1.5.3 auto_ptr
RAII:资源获得式初始化
1.7 异常规格说明
Unexcepted.cpp
/** * 书本:【ThinkingInC++】 * 功能:关于设置自己的异常函数,来显示不可预知的异常 * 时间:2014年10月8日20:20:06 * 作者:cutter_point */ #include <exception> #include <iostream> #include <cstdlib> using namespace std; class Up {}; class Fit {}; void g(); void f(int i) throw(Up, Fit) //根据给的int值来抛出异常 { switch(i) { case 1: throw Up(); case 2: throw Fit(); } g(); //本来在版本一的g()只是被声明的时候是不会抛出任何异常的,但是后面又抛出了一个int型的异常 } //定义函数g() 所以这里违反了异常抛出的规格(只有两种) void g() { throw 47; } void my_unexcepted() //这个用来设置在规格之外的异常抛出 { cout<<"规格之外的异常抛出,不是Up也不是Fit"<<endl; exit(0); } int main() { set_unexpected(my_unexcepted); //设置规格之外的异常将会调用的函数,忽略返回值 for(int i=1 ; i <= 3 ; ++i) try { f(i); } catch(Up) { cout<<"抛出Up类型的异常"<<endl; } catch(Fit) { cout<<"抛出Fit类型的异常"<<endl; } return 0; }
1.8 异常安全
对于赋值操作符。
1)确保程序不是给自己赋值。如果是的话,到步骤6
2)给指针数据成员分配所需的新内存
3)从原来的内存区间向新分配的内存区拷贝数据
4)释放原有的内存
5)更新对象的状态,也就是把指向分配新堆内存地址的指针赋值给指针数据成员
6)返回*this
关于句柄
从上面的定义中的我们可以看到,句柄是一个标识符,是拿来标识对象或者项目的,它就象我们的姓名一样,每个人都会有一个,不同的人的姓名不一样,但是,也可能有一个名字和你一样的人。从数据类型上来看它只是一个16位的无符号整数。
管理其他资源的类
Strlen
需要明确的第一点,strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符‘/0‘为止,然后返回计数器值。
SafeAssign.cpp
/** * 书本:【ThinkingInC++】 * 功能:关于operator=的安全分配内存 * 时间:2014年10月8日20:20:41 * 作者:cutter_point */ #include <iostream> #include <new> //为了抛出异常bad_alloc #include <cstring> #include <cstddef> using namespace std; class HasPointers { //这里设置一个句柄类来管理我们的数据 struct MyData { const char* theString; const int* theInts; size_t numInts; //构造函数 MyData(const char* pString, const int* pInts, size_t nInts) : theString(pString), theInts(pInts), numInts(nInts) {} }*theData; //这个指针是指向这个结构体的 //这里为了安全的分配内存,我们使用一个clone静态函数 static MyData* clone(const char* otherString, const int* otherInts, size_t nInts) { /* 需要明确的第一点,strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头, 中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符'/0'为止,然后返回计数器值。 */ char* newChars=new char[strlen(otherString)+1]; //吧这个字符串的长度加一,作为这个数组的长度,最后一位是'/0' int* newInts; //这里分配int类型的,避免异常 try { newInts=new int[nInts]; } catch(bad_alloc&) { delete []newChars; throw; } try { strcpy(newChars, otherString); //拷贝到newChars里面去 for(size_t i=0 ; i < nInts ; ++i) newInts[i]=otherInts[i]; //吧otherInts里面的数据赋值给newInts } catch(...) { delete []newInts; delete []newChars; throw; } return new MyData(newChars, newInts, nInts); } //一个重载的clone,拷贝构造函数 static MyData* clone(const MyData* otherData) { return clone(otherData->theString, otherData->theInts, otherData->numInts); } //内存清理 static void cleanup(const MyData* theData) { //回收全部内存,只要使用了指针的,都要把指针指向的内存回收掉 delete theData->theString; delete theData->theInts; delete theData; } public: //构造函数 HasPointers(const char* someString, const int* someInts, size_t numInts) { theData=clone(someString, someInts, numInts); } //拷贝构造函数 HasPointers(const HasPointers& source) { theData=clone(source.theData); } //赋值拷贝运算符 HasPointers& operator=(const HasPointers& rhs) { //避免自赋值 if(this != &rhs) { MyData* newData=clone(rhs.theData->theString, rhs.theData->theInts, rhs.theData->numInts); cleanup(theData); theData=newData; } return *this; } //析构函数 ~HasPointers() { cleanup(theData); } //友元函数 friend ostream& operator<<(ostream& os, const HasPointers& obj) { os<<obj.theData->theString<<" : "; for(size_t i=0 ; i < obj.theData->numInts ; ++i) os<<obj.theData->theInts[i]<<' '; return os; } }; int main() { int someNums[]={1, 2, 3, 4}; size_t someCount=sizeof(someNums)/sizeof(someNums[0]); int someMoreNums[]={5, 6, 7}; size_t someMoreCount=sizeof(someMoreNums)/sizeof(someMoreNums[0]); cout<<"这两个数组长度是:"<<someCount<<" : "<<someMoreCount<<endl; HasPointers h1("Hello", someNums, someCount); HasPointers h2("Goodbye", someMoreNums, someMoreCount); cout<<h1<<endl; h1=h2; cout<<h1<<endl; return 0; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。