C++程序的设计 机制3 RAII机制(2)
为了管理内存等资源,C++程序员通常采用RAII机制(资源获取即初始化),在使用资源的类的构造函数中申请资源,然后使用,最后在析构函数中释放资源。今天本文为你介绍RAII机制,一起来看。
AD:
2)智能指针模拟
一个更复杂一点的例子是模拟智能指针,抽象出来的RAII类中实现了一个操作符*,直接返回存入的指针:
现在我们有一个类:
1. class Example {
2. SomeResource* p_;
3. SomeResource* p2_;
4. public:
5. Example() :
6. p_(new SomeResource()),
7. p2_(new SomeResource()) {
8. std::cout << "Creating Example, allocating SomeResource!\n";
9. }
10. Example(const Example& other) :
11. p_(new SomeResource(*other.p_)),
12. p2_(new SomeResource(*other.p2_)) {}
13. Example& operator=(const Example& other) {
14. // Self assignment?
15. if (this==&other)
16. return *this;
17. *p_=*other.p_;
18. *p2_=*other.p2_;
19. return *this;
20. }
21. ~Example() {
22. std::cout << "Deleting Example, freeing SomeResource!\n";
23. delete p_;
24. delete p2_;
25. }
26. };
假设在创建SomeResource的时候可能会有异常,那么当p_指向的资源被创建但p2_指向的资源创建失败时,Example的实例就整个创建失败,那么p_指向的资源就存在内存泄露问题。
用下边的这个方法可以为权宜之计:
1. Example() : p_(0),p2_(0)
2. {
3. try {
4. p_=new SomeResource();
5. p2_=new SomeResource("H",true);
6. std::cout << "Creating Example, allocating SomeResource!\n";
7. }
8. catch(...) {
9. delete p2_;
10. delete p_;
11. throw;
12. }
13. }
但是我们可以利用一个对象在离开一个域中会调用析构函数的特性,在构造函数中完成初始化,在析构函数中完成清理工作,将需要操作和保护的指针作为成员变量放入RAII中。
1. template
2. class RAII {
3. T* p_;
4. public:
5. explicit RAII(T* p) : p_(p) {}
6. ~RAII() {
7. delete p_;
8. }
9. void reset(T* p) {
10. delete p_;
11. p_=p;
12. }
13. T* get() const {
14. return p_;
15. }
16. T& operator*() const {
17. return *p_;
18. }
19. void swap(RAII& other) {
20. std::swap(p_,other.p_);
21. }
22. private:
23. RAII(const RAII& other);
24. RAII& operator=(const RAII& other);
25. };
我们在具体使用把保护的指针Someresource放在RAII中:
1. class Example {
2. RAII p_;
3. RAII p2_;
4. public:
5. Example() :
6. p_(new SomeResource()),
7. p2_(new SomeResource()) {}
8. Example(const Example& other)
9. : p_(new SomeResource(*other.p_)),
10. p2_(new SomeResource(*other.p2_)) {}
11. Example& operator=(const Example& other) {
12. // Self assignment?
13. if (this==&other)
14. return *this;
15. *p_=*other.p_;
16. *p2_=*other.p2_;
17. return *this;
18. }
19. ~Example() {
20. std::cout << "Deleting Example, freeing SomeResource!\n";
21. }
22. };
现在即使p_成功而p2_失败,那么在Stack winding时也会调用RAII的析构函数保证了p_指向的Someresource被析构。这种方法较之例1中需要实现被组合的指针类型相应的接口不 同,这里不需要对接口进行封装。当然,在例1中,你也可以提供一个getPointer的函数直接将句柄提供出来。
其实在Example中,已经不需要析构函数了,因为RAII类会帮它照顾好这一切的。这有点像auto_ptr,本文并不打算深入讨论智能指针这个话题。参考资料http://wenku.it168.com/d_000773512.shtml