智能指针类型
- C++98最早的智能指针auto_ptr已被废止。
- C++11/14标准中的unique_ptr、shared_ptr和weak_ptr,源于boost中的scoped_ptr、shared_ptr和weak_ptr(boost中共有6种智能指针)
以boost 来说明
名称 | 头文件 | 说明 |
---|---|---|
scoped_ptr | <boost/scoped_ptr.hpp> | 简单的单一对象的唯一所有权。不可拷贝。 |
scoped_array | <boost/scoped_array.hpp> | 简单的数组的唯一所有权。不可拷贝。 |
shared_ptr | <boost/shared_ptr.hpp> | 在多个指针间共享的对象所有权。 |
shared_array | <boost/shared_array.hpp> | 在多个指针间共享的数组所有权。 |
weak_ptr | <boost/weak_ptr.hpp> | 一个属于 shared_ptr 的对象的无所有权的观察者。 |
intrusive_ptr | <boost/intrusive_ptr.hpp> | 带有一个侵入式引用计数的对象的共享所有权。 |
- scoped_ptr(scoped_array)
scoped_ptr包装了new在堆上的动态对象,能保证对象能在任何时候都被正确的删除。scoped_ptr的所有权更严格,不能转让。scoped_ptr,看名字,该智能指针只可以在本作用域内使用,不希望被转让。scoped_array和scoped_ptr的唯一不同是scoped_array管理数组对象。不建议使用scoped_array,可用vector替代。
template<class T> class scoped_ptr // noncopyable
{
private:
T * px;//原始指针
//禁止对智能指针的拷贝,保证被它管理的指针不被转让所有权。
//如果一个类持有scoped_ptr成员,它也是禁止拷贝赋值的。
scoped_ptr(scoped_ptr const &);//拷贝构造私有化
scoped_ptr & operator=(scoped_ptr const &);//赋值操作私有化
typedef scoped_ptr<T> this_type;
void operator==( scoped_ptr const& ) const;
void operator!=( scoped_ptr const& ) const;
public:
typedef T element_type;
explicit scoped_ptr( T * p = 0 ); // never throws
explicit scoped_ptr( std::auto_ptr<T> p );
~scoped_ptr(); // never throws
void reset(T * p = 0); // never throws,重置指针,一般不用
//模拟原始指针操作,保存空指针时2操作未定义
T & operator*() const; // never throws
T * operator->() const; // never throws
T * get() const;//获得原始指针
void swap(scoped_ptr & b);//交换指针
};
unique_ptr
std::unique_ptr是C++11中新定义的智能指针,用于取代auto_ptr。unique_ptr不仅可以代理new创建的单个对象,也可以代理new[]创建的数组对象,就是说它结合了scoped_ptr和scoped_array两者的能力。
unique_ptr的基本能力跟scoped_ptr一样,同样可以在作用域内管理指针,也不允许拷贝和赋值。shared_ptr
shared_ptr是最像指针的智能指针。shared_ptr和scoped_ptr一样包装了new在堆上的动态对象,也不可以管理new[]产生的数组指针,也没有指针算术操作。但是shared_ptr实现了引用计数,可以自由拷贝赋值,在任意地方共享它,当没有代码使用它(引用计数为0)时,它才删除被包装的动态对象。shared_ptr可以被放在标准容器中,STL容器存储指针的标准做法。
工厂函数make_shared
shared_ptr消除了显示调用delete,但是没有消除显示调用new,boost提供一个工厂函数make_shared来消除显式new调用。例子:
void fun()
{
auto sp = boost::make_shared<string>("make_shared");//创建string共享指针
auto spv = boost::make_shared< vector<int> >(10,2);//vector共享指针
}
- weak_ptr
weak_ptr是为了配合shared_ptr而引入的,它没有普通指针的行为,没重载*和->。它最大的作用是协助shared_ptr工作,像旁观者一样观测资源的使用情况。weak_ptrk可以从一个shared_ptr或者一个weak_ptr对象构造,获得对象资源观测权。但是weak_ptr并没有共享资源,它的构造不会引起引用计数的增加,也不会让引用计数减少,它只是静静的观察者。
template<class T> class weak_ptr
{
private:
typedef weak_ptr<T> this_type;
public:
weak_ptr();
~weak_ptr();
weak_ptr( weak_ptr const & r );
weak_ptr & operator=( weak_ptr const & r );
weak_ptr( weak_ptr<Y> const & r );
shared_ptr<T> lock() const;//获取shared_ptr,把弱关系转为强关系,操作资源
long use_count() const;//引用计数
bool expired() const;//是否失效指针
void reset();//重置指针
void swap(this_type & other);
element_type * px; // contained pointer
boost::detail::weak_count pn; // reference counter
}; // weak_ptr
template<class T, class U> inline bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b);
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b);
举例:
class self_shared: public enable_shared_from_this<self_shared>
{
public:
int n;
void fun();
};
int main()
{
auto sp = make_shared<self_shared>(123);
sp->fun();
auto p = sp->shared_from_this();
p->fun();
//不能对普通对象使用shared_from_this
self_shared s;
auto ps = s->shared_from_this();//错误,shared_ptr会在析构时试图删除一个栈对象
return 0;
}
打破循环引用:
有时候代码会出现循环引用,这时候shared_ptr的引用计数就会失效,导致不能正确释放资源。例如:
class node
{
public:
~node(){cout<<"deleted.\n";}
typedef shared_ptr<node> ptr_type;
ptr_type next;
};
int main()
{
auto p1 = make_shared<node>();
auto p2 = make_shared<node>();
p1->next = p2;//形成循环链表
p2->next = p1;
cout<<p1->use_count()<<endl;//=2
cout<<p2->use_count()<<endl;//=2
return 0;
}
两个node对象都互相持有对方的引用,每个shared_ptr对象的引用计数都是2,因此在析构时不能减为0,不会调用删除操作,导致内存泄漏。这个时候我们可以使用weak_ptr,因为它不会增加引用计数,这强引用变为弱引用,在可能循环的地方打破循环,真正需要shared_ptr的时候调用weak_ptr的lock()函数。
//修改以上
typedef weak_ptr<node> ptr_type;
p1->next = p2;//形成循环链表
p2->next = p1;//weak_ptr所以正常
if (!p1->next->expired())//检查弱引用是否有效
{
auto p3 = p1->next->lock();//获得强引用
}
//退出后,shared_ptr都正确析构
Tips
关于auto_ptr的几种注意事项:
1、auto_ptr不能共享所有权。
2、auto_ptr不能指向数组
3、auto_ptr不能作为容器的成员。
4、不能通过赋值操作来初始化auto_ptr
std::auto_ptr<int> p(new int(42)); //OK
std::auto_ptr<int> p = new int(42); //ERROR
这是因为auto_ptr 的构造函数被定义为了explicit
5、不要把auto_ptr放入容器