[记] C++智能指针

智能指针类型

  • 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> 带有一个侵入式引用计数的对象的共享所有权。
  1. 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);//交换指针
};
  1. unique_ptr
    std::unique_ptr是C++11中新定义的智能指针,用于取代auto_ptr。unique_ptr不仅可以代理new创建的单个对象,也可以代理new[]创建的数组对象,就是说它结合了scoped_ptr和scoped_array两者的能力。
    unique_ptr的基本能力跟scoped_ptr一样,同样可以在作用域内管理指针,也不允许拷贝和赋值。

  2. 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共享指针
}
  1. 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放入容器

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • C++ 智能指针详解 一、简介由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 de...
    yangqi916阅读 1,352评论 0 2
  • 导读## 最近在补看《C++ Primer Plus》第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰...
    小敏纸阅读 1,977评论 1 12
  • 1. 什么是智能指针? 智能指针是行为类似于指针的类对象,但这种对象还有其他功能。 2. 为什么设计智能指针? 引...
    MinoyJet阅读 632评论 0 1
  • C#、Java、python和go等语言中都有垃圾自动回收机制,在对象失去引用的时候自动回收,而且基本上没有指针的...
    StormZhu阅读 3,678评论 1 15
  • 原作者:Babu_Abdulsalam 本文翻译自CodeProject,转载请注明出处。 引入### Ooops...
    卡巴拉的树阅读 30,042评论 13 74