Thinking in C++ 学习笔记(2)
关于C++ 中的RAII(Resource Acquisition In Initialisation) Wrapper
问题:class的构造函数中如果出现异常而被捕获中止,则对象构造不完整, 相应的析构函数也不会被调用。 如果在构造的异常出现之前已经有指向其它对象的堆指针被实例化, 由于该对象的析构函数
不被调用,而使得对象的堆指针指出的堆内存不被释放。
如:
1 //: C01:Rawp.cpp
2 //Naked pointer
3
4 #include <iostream>
5 #include <cstddef>
6
7 using namespace std;
8
9 class Cat {
10 public:
11 Cat() {cout << "Cat ()" << endl;}
12 virtual ~Cat() {cout << "~Cat()" << endl;}
13 };
14
15 class Dog {
16 public:
17 void *operator new(size_t sz) {
18 cout << "allocating a Dog" << endl;
19 throw 47;
20 }
21
22 void operator delete(void* p) {
23 cout << "deallocating a Dog" << endl;
24 ::operator delete(p);
25 }
26 };
27
28 class UseResource {
29 Cat* bp;
30 Dog* op;
31 public:
32 UseResource(int count = 1) {
33 cout << "UseResources()" << endl;
34 bp = new Cat[count];
35 op = new Dog;
36 }
37 virtual ~UseResource() {
38 cout << "~UseResource()" << endl;
39 delete[] bp;
40 delete op;
41 }
42 };
43
44 int main()
45 {
46 try {
47 UseResource ur(3);
48 } catch (int) {
49 cout << "inside handler" << endl;
50 }
51 }
解决方法:
1 在构造函数中使用try{} catch() {}捕获异常并且进行处理。
2 将nake pointer的分配内存过程由另外一个对象的构造函数来实现,
而释放内存过程由另外一个对象的析构函数来完成。
对于2的实例如下:
1 //: C01:Wrapped.cpp
2 // Saft atomic pointers
3
4 #include <iostream>
5 #include <cstddef>
6 using namespace std;
7
8 template<class T, int sz = 1>
9 class PWrap {
10 T* ptr;
11 public:
12
13 class RangeError{};
14 PWrap() {
15 ptr = new T[sz];
16 cout << "PWrap Constructor" << endl;
17 }
18 virtual ~PWrap () {
19 delete[] ptr;
20 cout << "PWrap Destructor" << endl;
21 }
22
23 T& operator [] (int i) throw (RangeError) {
24 if (i >= 0 && i < sz) {
25 return ptr[i];
26 }
27 throw RangeError();
28 }
29 };
30
31
32 class Cat {
33 public:
34 Cat() {cout << "Cat()" << endl;}
35 virtual ~Cat() {cout << "~Cat" << endl;}
36 void g() {};
37 };
38
39 class Dog {
40 public :
41 void *operator new[] (size_t ) {
42 cout << "allocating a Dog" << endl;
43 throw 47;
44 }
45
46 void operator delete[](void* p) {
47 cout << "Deallocating a Dog" << endl;
48 ::operator delete[] (p);
49 }
50 };
51
52 class UseResource {
53 PWrap<Cat, 3> cats;
54 PWrap<Dog> dog;
55 public:
56 UseResource() { cout << "UseResource()" << endl;}
57 virtual ~UseResource() { cout << "~UseResource()" << endl;}
58 void f() { cats[1].g();}
59 };
60
61 int main() {
62 try {
63 UseResource ur;
64 } catch (int) {
65 cout << "inside handler" << endl;
66 }
67 }
在c++中,<memory>中的auto_ptr 就是这样一个RAII Wrapper模板。
其使用方式如下:
1 //: C01:Auto_ptr.cpp
2 // auto_ptr template for wrapping raw pointer
3
4 #include <iostream>
5 #include <cstddef> //for size_t type
6
7 using namespace std;
8
9 class TraceHeap {
10 int i;
11 public:
12 static void * operator new (size_t size) {
13 void * p = ::operator new(size);
14 cout << "Allocating TraceHeap object on the heap"
15 << " at address "
16 << p << endl;
17 return p;
18 }
19
20 static void operator delete(void* p) {
21 cout << "Deallocating TraceHeap object at address "
22 << p << endl;
23 ::operator delete(p);
24 }
25
26 TraceHeap(int i) : i(i) {
27
28 }
29
30 int getVal() const {return i;}
31 };
32
33 int main() {
34 auto_ptr<TraceHeap> pMyObject(new TraceHeap(3));
35 cout << pMyObject->getVal() << endl;
36 }
运行结果:
Allocating TraceHeap object on the heap at address 0x502010
3
Deallocating TraceHeap object at address 0x502010