C++程序的设计机制3 RAII机制(2)
为了管理内存等资源,C++程序员通常采用RAII机制(资源获取即初始化),在使用资源的类的构造函数中申请资源,然后使用,最后在析构函数中释放资源。今天本文为你介绍RAII机制,一起来看。
AD:
2)智能指针模拟
一个更复杂一点的例子是模拟智能指针,抽象出来的RAII类中实现了一个操作符*,直接返回存入的指针:
现在我们有一个类:
- class Example {
- SomeResource* p_;
- SomeResource* p2_;
- public:
- Example() :
- p_(new SomeResource()),
- p2_(new SomeResource()) {
- std::cout << "Creating Example, allocating SomeResource!\n";
- }
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_指向的资源就存在内存泄露问题。
用下边的这个方法可以为权宜之计:
- Example() : p_(0),p2_(0)
- {
- try {
- p_=new SomeResource();
- p2_=new SomeResource("H",true);
- std::cout << "Creating Example, allocating SomeResource!\n";
- }
- catch(...) {
- delete p2_;
10.delete p_;
11.throw;
12.}
13.}
但是我们可以利用一个对象在离开一个域中会调用析构函数的特性,在构造函数中完成初始化,在析构函数中完成清理工作,将需要操作和保护的指针作为成员变量放入RAII中。
- template
- class RAII {
- T* p_;
- public:
- explicit RAII(T* p) : p_(p) {}
- ~RAII() {
- delete p_;
- }
- 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中:
- class Example {
- RAII p_;
- RAII p2_;
- public:
- Example() :
- p_(new SomeResource()),
- p2_(new SomeResource()) {}
- Example(const Example& other)
- : 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,本文并不打算深入讨论智能指针这个话题。参考资料