C++程序的设计机制3 RAII机制(2)

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

posted @ 2013-01-30 10:03  最轻  阅读(148)  评论(0编辑  收藏  举报