[C++] Boost智能指针——boost::scoped_ptr(使用及原理分析)

简介

     boost::scoped_ptr是一个比较简单的智能指针,它能保证在离开作用域之后它所管理对象能被自动释放。下面这个例子将介绍它的使用:

 1 #include <iostream>
 2 #include <boost/scoped_ptr.hpp>
 3 
 4 using namespace std;
 5 
 6 class Book
 7 {
 8 public:
 9     Book()
10     {
11         cout << "Creating book ..." << endl;
12     }
13 
14     ~Book()
15     {
16         cout << "Destroying book ..." << endl;
17     }
18 };
19 
20 int main()
21 {   
22     cout << "=====Main Begin=====" << endl;
23     {
24         boost::scoped_ptr<Book> myBook(new Book());
25     }
26     cout << "===== Main End =====" << endl;
27 
28     return 0;
29 }

     运行结果:

     可以看出:当myBook离开了它的作用域之后,它所管理的Book对象也随之销毁。

 

特点——不能共享控制权

     scoped_ptr不能通过其他scoped_ptr共享控制权,因为在scoped_ptr类的内部将拷贝构造函数=运算符重载定义为私有的。我们看下scoped_ptr类的定义就清楚了:

 1 namespace boost
 2 {
 3     template<typename T> class scoped_ptr : noncopyable
 4     {
 5     private:
 6 
 7         T *px;
 8 
 9         scoped_ptr(scoped_ptr const &);
10         scoped_ptr &operator=(scoped_ptr const &);
11 
12         typedef scoped_ptr<T> this_type;
13 
14         void operator==( scoped_ptr const & ) const;
15         void operator!=( scoped_ptr const & ) const;
16     public:
17         explicit scoped_ptr(T *p = 0);
18         ~scoped_ptr();
19 
20         explicit scoped_ptr( std::auto_ptr<T> p ): px( p.release() );
21         void reset(T *p = 0);
22 
23         T &operator*() const;
24         T *operator->() const;
25         T *get() const;
26 
27         void swap(scoped_ptr &b);
28     };
29 
30     template<typename T>
31     void swap(scoped_ptr<T> &a, scoped_ptr<T> &b);
32 }

 

     下面这段代码中的注释部分打开会造成编译失败:

 1 #include <iostream>
 2 #include <boost/scoped_ptr.hpp>
 3 
 4 using namespace std;
 5 
 6 class Book
 7 {
 8 public:
 9     Book()
10     {
11         cout << "Creating book ..." << endl;
12     }
13 
14     ~Book()
15     {
16         cout << "Destroying book ..." << endl;
17     }
18 };
19 
20 int main()
21 {   
22     cout << "=====Main Begin=====" << endl;
23     {
24         boost::scoped_ptr<Book> myBook(new Book());
25         //boost::scoped_ptr<Book> myBook1(myBook);    // Error: scoped_ptr的拷贝构造函数私有
26         //boost::scoped_ptr<Book> myBook2 = myBook;   // Error: scoped_ptr的=运算符重载私有
27     }
28     cout << "===== Main End =====" << endl;
29 
30     return 0;
31 }

 

    所以,scoped_ptr不能用在标准库的容器中,因为容器中的push_back操作需要调用scoped_ptr的=运算符重载函数,结果就是会导致编译失败。

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 #include <boost/scoped_ptr.hpp>
 5 
 6 using namespace std;
 7 
 8 class Book
 9 {
10 private:
11     string name_;
12 
13 public:
14     Book(string name) : name_(name)
15     {
16         cout << "Creating book " << name_ << " ..." << endl;
17     }
18 
19     ~Book()
20     {
21         cout << "Destroying book " << name_ << " ..." << endl;
22     }
23 };
24 
25 int main()
26 {   
27     cout << "=====Main Begin=====" << endl;
28     {
29         boost::scoped_ptr<Book> myBook(new Book("「1984」"));
30         vector<boost::scoped_ptr<Book>> vecScoped;
31         //vecScoped.push_back(myBook);   // Error: push_back操作内部调用了scoped_ptr的=运算符重载函数
32     }
33     cout << "===== Main End =====" << endl;
34 
35     return 0;
36 }

 

编译检查=万无一失?

     虽然我们无法通过scoped_ptr的拷贝构造函数和=运算符重载函数共享控制权。那如果将一个对象交给多个scoped_ptr来管理会怎样?

 1 #include <iostream>
 2 #include <boost/scoped_ptr.hpp>
 3 
 4 using namespace std;
 5 
 6 class Book
 7 {
 8 public:
 9     Book()
10     {
11         cout << "Creating book ..." << endl;
12     }
13 
14     ~Book()
15     {
16         cout << "Destroying book ..." << endl;
17     }
18 };
19 
20 int main()
21 {   
22     cout << "=====Main Begin=====" << endl;
23     {
24         Book * book = new Book();
25         boost::scoped_ptr<Book> myBook(book);
26         boost::scoped_ptr<Book> myBook1(book);
27     }
28     cout << "===== Main End =====" << endl;
29 
30     return 0;
31 }

      我们发现编译没报错,但是运行时出错了,如下:

     之所以会这样是因为每个scoped_ptr对象都保存了自己所管理对象指针px,scoped_ptr对象在离开自己作用域时会调用了自身的析构函数,在析构函数内部会调用delete px,当多个scoped_ptr管理同一个对象时,那么在它们离开作用域之后,势必会多次调用delete以释放它们所管理的对象,从而造成程序运行出错。

 

其他接口

     虽然scoped_ptr不能转移控制权,但是它们可以交换共享权。就以下面的代码举个例子:

 1 #include <iostream>
 2 #include <string>
 3 #include <boost/scoped_ptr.hpp>
 4 
 5 using namespace std;
 6 
 7 class Book
 8 {
 9 private:
10     string name_;
11 
12 public:
13     Book(string name) : name_(name)
14     {
15         cout << "Creating book " << name_ << " ..." << endl;
16     }
17 
18     ~Book()
19     {
20         cout << "Destroying book " << name_ << " ..." << endl;
21     }
22 };
23 
24 int main()
25 {   
26     cout << "=====Main Begin=====" << endl;
27     {
28         boost::scoped_ptr<Book> myBook(new Book("「1984」"));
29         boost::scoped_ptr<Book> myBook1(new Book("「A Song of Ice and Fire」"));
30         myBook.swap(myBook1);
31     }
32     cout << "===== Main End =====" << endl;
33 
34     return 0;
35 }

      运行结果:

     根据栈的特性,应该是后面构造的scoped_ptr对象先销毁(从而销毁了它们所管理的对象),正是因为我们对两个智能指针的控制权进行交换之后,才出现了这种相反的结果。

 

     此外,在scoped_ptr离开作用域之前也是可以显式销毁它们所管理的对象的。调用它的reset方法即可。请看下面例子:

 1 #include <iostream>
 2 #include <string>
 3 #include <boost/scoped_ptr.hpp>
 4 
 5 using namespace std;
 6 
 7 class Book
 8 {
 9 private:
10     string name_;
11 
12 public:
13     Book(string name) : name_(name)
14     {
15         cout << "Creating book " << name_ << " ..." << endl;
16     }
17 
18     ~Book()
19     {
20         cout << "Destroying book " << name_ << " ..." << endl;
21     }
22 };
23 
24 int main()
25 {   
26     cout << "=====Main Begin=====" << endl;
27     {
28         boost::scoped_ptr<Book> myBook(new Book("「1984」"));
29         myBook.reset();
30         cout << "After reset ..." << endl;
31     }
32     cout << "===== Main End =====" << endl;
33 
34     return 0;
35 }

      运行结果:

     可以看出:程序在输出“After reset ...”之前已经完成了对所管理对象的释放。

 

总结(摘自《超越C++标准库:Boost库导论》)

     使用裸指针来写异常安全和无错误的代码是很复杂的。使用智能指针来自动地把动态分配对象的生存期限制在一个明确的范围之内,是解决这种问题的一个有效的方法,并且提高了代码的可读性、可维护性和质量。scoped_ptr明确地表示被指物不能被共享和转移。当一个动态分配的对象被传送给 scoped_ptr, 它就成为了这个对象的唯一的拥有者。因为scoped_ptr几乎总是以自动变量或数据成员来分配的,因此它可以在离开作用域时正确地销毁,从而在执行流由于返回语句或异常抛出而离开作用域时,总能释放它所管理的内存。

     在以下情况时使用scoped_ptr:

  • 在可能有异常抛出的作用域里使用指针
  • 函数里有几条控制路径
  • 动态分配对象的生存期应被限制于特定的作用域内
  • 异常安全非常重要时(始终如此!)

 

参考

 

(完)

posted @ 2014-02-28 22:45  helloamigo  阅读(12190)  评论(0编辑  收藏  举报