下面的代码来自于VC++ 8.0里面的源码:
下面的例程来自Exceptional C++,Item 37:
//
void g()
{
T* pt1 = new T;
// right now, we own the allocated object
// pass ownership to an auto_ptr
auto_ptr<T> pt2( pt1 );
// use the auto_ptr the same way
// we'd use a simple pointer
*pt2 = 12; // same as "*pt1 = 12;"
pt2->SomeFunc(); // same as "pt1->SomeFunc();"
// use get() to see the pointer value
assert( pt1 == pt2.get() );
// use release() to take back ownership
T* pt3 = pt2.release();
// delete the object ourselves, since now
// no auto_ptr owns it any more
delete pt3;
} // pt2 doesn't own any pointer, and so won't
// try to delete it... OK, no double delete
// Example 3: Using reset()
//
void h()
{
auto_ptr<T> pt( new T(1) );
pt.reset( new T(2) );
// deletes the first T that was
// allocated with "new T(1)"
} // finally, pt goes out of scope and
// the second T is also deleted
: _Myptr(_Right.release())
{ // construct by assuming pointer from _Right auto_ptr
}
template < class _Other >
auto_ptr < _Ty >& operator = (auto_ptr < _Other >& _Right) _THROW0()
{ // assign compatible _Right (assume pointer)
reset(_Right.release());
return (*this);
}
// a non-owning auto_ptr
//
void f()
{
auto_ptr<T> pt1( new T );
auto_ptr<T> pt2;
pt2 = pt1; // now pt2 owns the pointer, and
// pt1 does not
pt1->DoSomething();
// error: following a null pointer
}
1、auto_ptr不能共享所有权
An important goal of shared_ptr is to provide a standard shared-ownership pointer.
<!--[if !vml]--><!--[endif]-->
public:
typedef T element_type;
shared_ptr(); // never throws
template<class Y> explicit shared_ptr(Y * p);
template<class Y, class D> shared_ptr(Y * p, D d);
~shared_ptr(); // never throws
shared_ptr(shared_ptr const & r); // never throws
template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
shared_ptr & operator=(shared_ptr const & r); // never throws
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
void reset(); // never throws
template<class Y> void reset(Y * p);
template<class Y, class D> void reset(Y * p, D d);
T & operator*() const; // never throws
T * operator->() const; // never throws
T * get() const; // never throws
bool unique() const; // never throws
long use_count() const; // never throws
operator unspecified-bool-type() const; // never throws
void swap(shared_ptr & b); // never throws
} ;
Note:
Boost文档里面的QA说明了为什么不提供release函数
Q. Why doesn ' t shared_ptr provide a release() function?
A. shared_ptr cannot give away ownership unless it ' s unique() because the other copy will still destroy
the object.
Consider:
shared_ptr < int > a( new int );
shared_ptr < int > b(a); // a.use_count() == b.use_count() == 2
int * p = a.release();
// Who owns p now? b will still call delete on it in its destructor.
Furthermore, the pointer returned by release() would be difficult to deallocate reliably,
as the source shared_ptr could have been created with a custom deleter.
另外,还记得Effective C++里面(或者其它的C++书籍),Scott Meyer告诉你的:在一个由多个模块组成的系统里面,一个模块不用试图自己去释放另外一个模块分配的资源,而应该遵循谁分配谁释放的原则。正确的原则但是有时难免有时让人忽略(过于繁琐),将资源包装在shared_ptr里面传递,而shared_ptr保证了在资源不再被拥有的时候,产生资源的模块的delete语句会被调用。
{
}
class Derived : public Base
{
}
shared_ptr < Base > sp_base( new Derived);
shared_ptr < Derived > sp_derived(pd);
shared_ptr < Base > sp_base2(sp_derived);
xxx_ptr < A > ptr_a_1(pa);
xxx_ptr < A > ptr_a_2(pa);
template <class Y> explicit shared_ptr(Y* p);
这个构造函数获得给定指针p的所有权。参数 p 必须是指向Y 的有效指针。构造后引用计数设为1。唯一从这个构造函数抛出的异常是std::bad_alloc (仅在一种很罕见的情况下发生,即不能获得引用计数器所需的自由空间)。
shared_ptr(const shared_ptr& r);
r中保存的资源被新构造的shared_ptr所共享,引用计数加一。这个构造函数不会抛出异常。
{
//do something
}
class A
{
doSomething()
{
xxx_ptr<A> ptr_a(this);
DoSomething(ptr_a);
}
} ;
int main()
{
A a;
a.doSomething();
//continue do something with a, but it was already destory
}
weak_ptr
- template<class T> class weak_ptr
- {
- public:
- weak_ptr();
- template<class Y> weak_ptr(shared_ptr<Y> const & r);
- weak_ptr(weak_ptr const & r);
- ~weak_ptr();
- weak_ptr & operator=(weak_ptr const & r);
- long use_count()const;
- //判断是否过期
- bool expired()const;
- //得到一个空的或者被协助的shared_ptr
- shared_ptr<T> lock()const;
- void reset();
- void swap(weak_ptr<T> & b);
- };
- template<class T> class weak_ptr
- {
- public:
- weak_ptr();
- template<class Y> weak_ptr(shared_ptr<Y> const & r);
- weak_ptr(weak_ptr const & r);
- ~weak_ptr();
- weak_ptr & operator=(weak_ptr const & r);
- long use_count()const;
- //判断是否过期
- bool expired()const;
- //得到一个空的或者被协助的shared_ptr
- shared_ptr<T> lock()const;
- void reset();
- void swap(weak_ptr<T> & b);
- };
- #include<iostream>
- #include<boost/weak_ptr.hpp>
- #include<boost/shared_ptr.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- shared_ptr<int> p(new int(10));
- weak_ptr<int> w(p);
- while(!w.expired()){
- cout << w.use_count() << endl;
- shared_ptr<int> t = w.lock();
- cout << *t << endl;
- cout << w.use_count() << endl;
- if(w.use_count()==2){
- break;
- }
- }
- w.reset();
- cout << w.expired() << endl;
- }
- 1
- 10
- 2
- 1
- #include<iostream>
- #include<boost/weak_ptr.hpp>
- #include<boost/shared_ptr.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- shared_ptr<int> p(new int(10));
- weak_ptr<int> w(p);
- while(!w.expired()){
- cout << w.use_count() << endl;
- shared_ptr<int> t = w.lock();
- cout << *t << endl;
- cout << w.use_count() << endl;
- if(w.use_count()==2){
- break;
- }
- }
- w.reset();
- cout << w.expired() << endl;
- }
- 1
- 10
- 2
- 1
weak_ptr的一个重要用途是通过lock获得this指针的shared_ptr,使对象自己能够生产shared_ptr来管理自己,但助手类enable_shared_from_this的shared_from_this会返回this的shared_ptr,只需要让想被shared_ptr管理的类从它继承即可
- #include<boost/enable_shared_from_this.hpp>
- #include<boost/make_shared.hpp>
- #include<iostream>
- using namespace boost;
- using namespace std;
- class testWeak:public enable_shared_from_this<testWeak>{//
- public:
- int i;
- testWeak(int ii):i(ii){}
- void print(){
- cout << i << endl;
- }
- };
- int main()
- {
- shared_ptr<testWeak> sp = make_shared<testWeak>(100);
- shared_ptr<testWeak> tw = sp->shared_from_this();
- tw->print();
- }
循环引用
引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象。一个简单的例子如下:
- #include <string>
- #include <iostream>
- #include <boost/shared_ptr.hpp>
- #include <boost/weak_ptr.hpp>
- class parent;
- class children;
- typedef boost::shared_ptr<parent> parent_ptr;
- typedef boost::shared_ptr<children> children_ptr;
- class parent
- {
- public:
- ~parent() { std::cout <<"destroying parent\n"; }
- public:
- children_ptr children;
- };
- class children
- {
- public:
- ~children() { std::cout <<"destroying children\n"; }
- public:
- parent_ptr parent;
- };
- void test()
- {
- parent_ptr father(new parent());
- children_ptr son(new children);
- father->children = son;
- son->parent = father;
- }
- void main()
- {
- std::cout<<"begin test...\n";
- test();
- std::cout<<"end test.\n";
- }
- #include <string>
- #include <iostream>
- #include <boost/shared_ptr.hpp>
- #include <boost/weak_ptr.hpp>
- class parent;
- class children;
- typedef boost::shared_ptr<parent> parent_ptr;
- typedef boost::shared_ptr<children> children_ptr;
- class parent
- {
- public:
- ~parent() { std::cout <<"destroying parent\n"; }
- public:
- children_ptr children;
- };
- class children
- {
- public:
- ~children() { std::cout <<"destroying children\n"; }
- public:
- parent_ptr parent;
- };
- void test()
- {
- parent_ptr father(new parent());
- children_ptr son(new children);
- father->children = son;
- son->parent = father;
- }
- void main()
- {
- std::cout<<"begin test...\n";
- test();
- std::cout<<"end test.\n";
- }
运行该程序可以看到,即使退出了test函数后,由于parent和children对象互相引用,它们的引用计数都是1,不能自动释放,并且此时这两个对象再无法访问到。这就引起了c++中那臭名昭著的内存泄漏。
一般来讲,解除这种循环引用有下面有三种可行的方法:
- 当只剩下最后一个引用的时候需要手动打破循环引用释放对象。
- 当parent的生存期超过children的生存期的时候,children改为使用一个普通指针指向parent。
- 使用弱引用的智能指针打破这种循环引用。
虽然这三种方法都可行,但方法1和方法2都需要程序员手动控制,麻烦且容易出错。这里主要介绍一下第三种方法和boost中的弱引用的智能指针boost::weak_ptr。
强引用和弱引用
对于强引用来说,如果被引用的对象还活着,那么这个强引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。
相对而言,对于弱引用来说,当引用的对象活着的时候弱引用不一定存在。仅仅是当它存在的时候的一个引用。弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。
boost::weak_ptr
boost::weak_ptr<T>是boost提供的一个弱引用的智能指针,它的声明可以简化如下:
- namespace boost {
- template<typename T> class weak_ptr {
- public:
- template <typename Y>
- weak_ptr(const shared_ptr<Y>& r);
- weak_ptr(const weak_ptr& r);
- ~weak_ptr();
- T* get() const;
- bool expired() const;
- shared_ptr<T> lock() const;
- };
- }
- namespace boost {
- template<typename T> class weak_ptr {
- public:
- template <typename Y>
- weak_ptr(const shared_ptr<Y>& r);
- weak_ptr(const weak_ptr& r);
- ~weak_ptr();
- T* get() const;
- bool expired() const;
- shared_ptr<T> lock() const;
- };
- }
可以看到,boost::weak_ptr必须从一个boost::shared_ptr或另一个boost::weak_ptr转换而来。这也说明,进行该对象的内存管理的是那个强引用的boost::shared_ptr。boost::weak_ptr只是提供了对管理对象的一个访问手段。
boost::weak_ptr除了对所管理对象的基本访问功能(通过get()函数)外,还有两个常用的功能函数:expired()用于检测所管理的对象是否已经释放;lock()用于获取所管理的对象的强引用指针。
通过boost::weak_ptr来打破循环引用
由于弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用。对于上面的那个例子来说,只要把children的定义改为如下方式,即可解除循环引用:
- class children
- {
- public:
- ~children() { std::cout <<"destroying children\n"; }
- public:
- boost::weak_ptr<parent> parent;
- };
- class children
- {
- public:
- ~children() { std::cout <<"destroying children\n"; }
- public:
- boost::weak_ptr<parent> parent;
- };
最后值得一提的是,虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案,如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。因此,不要认为只要使用了智能指针便能杜绝内存泄漏。毕竟,对于C++来说,由于没有垃圾回收机制,内存泄漏对每一个程序员来说都是一个非常头痛的问题。
scoped_ptr
头文件: "boost/scoped_ptr.hpp"
boost::scoped_ptr 用于确保动态分配的对象能够被正确地删除。scoped_ptr 有着与std::auto_ptr类似的特性,而最大的区别在于它不能转让所有权而auto_ptr可以。事实上,scoped_ptr永远不能被复制或被赋值!scoped_ptr 拥有它所指向的资源的所有权,并永远不会放弃这个所有权。scoped_ptr的这种特性提升了我们的代码的表现,我们可以根据需要选择最合适的智能指针(scoped_ptr 或 auto_ptr)。
要决定使用std::auto_ptr还是boost::scoped_ptr, 就要考虑转移所有权是不是你想要的智能指针的一个特性。如果不是,就用scoped_ptr. 它是一种轻量级的智能指针;使用它不会使你的程序变大或变慢。它只会让你的代码更安全,更好维护。
下面是scoped_ptr的摘要,以及其成员的简要描述:
namespace boost { template<typename T> class scoped_ptr : noncopyable { public: explicit scoped_ptr(T* p = 0); ~scoped_ptr(); void reset(T* p = 0); T& operator*() const; T* operator->() const; T* get() const; void swap(scoped_ptr& b); }; template<typename T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); }
成员函数
explicit scoped_ptr(T* p=0)
构造函数,存储p的一份拷贝。注意,p 必须是用operator new分配的,或者是null. 在构造的时候,不要求T必须是一个完整的类型。当指针p是调用某个分配函数的结果而不是直接调用new得到的时候很有用:因为这个类型不必是完整的,只需要类型T的一个前向声明就可以了。这个构造函数不会抛出异常。
~scoped_ptr()
删除被指物。类型T在被销毁时必须是一个完整的类型。如果scoped_ptr在它被析构时并没有保存资源,它就什么都不做。这个析构函数不会抛出异常。
void reset(T* p=0);
重置一个 scoped_ptr 就是删除它已保存的指针,如果它有的话,并重新保存 p. 通常,资源的生存期管理应该完全由scoped_ptr自己处理,但是在极少数时候,资源需要在scoped_ptr的析构之前释放,或者scoped_ptr要处理它原有资源之外的另外一个资源。这时,就可以用reset,但一定要尽量少用它。(过多地使用它通常表示有设计方面的问题) 这个函数不会抛出异常。
T& operator*() const;
返回一个到被保存指针指向的对象的引用。由于不允许空的引用,所以解引用一个拥有空指针的scoped_ptr将导致未定义行为。如果不能肯定所含指针是否有效,就用函数get替代解引用。这个函数不会抛出异常。
T* operator->() const;
返回保存的指针。如果保存的指针为空,则调用这个函数会导致未定义行为。如果不能肯定指针是否空的,最好使用函数get。这个函数不会抛出异常。
T* get() const;
返回保存的指针。应该小心地使用get,因为它可以直接操作裸指针。但是,get使得你可以测试保存的指针是否为空。这个函数不会抛出异常。get通常在调用那些需要裸指针的函数时使用。
operator unspecified_bool_type() const
返回scoped_ptr是否为非空。返回值的类型是未指明的,但这个类型可被用于Boolean的上下文中。在if语句中最好使用这个类型转换函数,而不要用get去测试scoped_ptr的有效性
void swap(scoped_ptr& b)
交换两个scoped_ptr的内容。这个函数不会抛出异常。
普通函数
template<typename T> void swap(scoped_ptr<T>& a,scoped_ptr<T>& b)
这个函数提供了交换两个scoped pointer的内容的更好的方法。之所以说它更好,是因为 swap(scoped1,scoped2)可以更广泛地用于很多指针类型,包括裸指针和第三方的智能指针。scoped1.swap(scoped2) 则只能用于它的定义所在的智能指针,而不能用于裸指针。
用法你可为那些不够智能,没有提供它们自己的交换函数的智能指针创建你的普通swap函数。
scoped_ptr的用法与普通的指针没什么区别;最大的差别在于你不必再记得在指针上调用delete,还有复制是不允许的。典型的指针操作(operator* 和 operator->)都被重载了,并提供了和裸指针一样的语法。用scoped_ptr和用裸指针一样快,也没有大小上的增加,因此它们可以广泛使用。使用boost::scoped_ptr时,包含头文件"boost/scoped_ptr.hpp". 在声明一个scoped_ptr时,用被指物的类型来指定类模板的参数。例如,以下是一个包含std::string指针的scoped_ptr:
boost::scoped_ptr<std::string> p(new std::string("Hello"));
当scoped_ptr被销毁时,它对它所拥有的指针调用delete 。
scoped_ptr 不同于 const auto_ptr
留心的读者可能已经注意到auto_ptr可以几乎象scoped_ptr一样地工作,只要把auto_ptr声明为const:
const auto_ptr<A> no_transfer_of_ownership(new A);
它们很接近,但不是一样。最大的区别在于scoped_ptr可以被reset, 在需要时可以删除并替换被指物。而对于const auto_ptr这是不可能的。另一个小一点的区别是,它们的名字不同:尽管const auto_ptr意思上和scoped_ptr一样,但它更冗长,也更不明显。当你的词典里有了scoped_ptr,你就应该使用它,因为它可以更清楚地表明你的意图。如果你想说一个资源是要被限制在作用域里的,并且不应该有办法可以放弃它的所有权,你就应该用 boost::scoped_ptr.