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,本文并不打算深入讨论智能指针这个话题。参考资料