智能指针

一、概述

智能指针是一个类,主要用于管理堆上分配的内存,它将普通的指针封装成一个栈对象。在栈对象的生命周期结束后,会在析构函数中释放申请的内存,防止内存泄漏。
在使用智能指针的时候,需要引入头文件<memory>。

1、作用:

智能指针是管理一个普通指针,在函数结束时自动释放内存空间,不需要手动释放内存空间。避免申请的内存空间在函数结束时忘记释放,造成的内存泄漏。

2、智能指针类型

1、auto_ptr:

c++98的方案,c++11已经抛弃,采用所有权模式——(一个对象只能对应一个auto_ptr 指针,当拷贝时,会将对象转移到新的指针所持有,之前的指针不再持有)。

void testAutoPtrFunc()
{
    auto_ptr<string> p1 (new string("This is a auto_ptr!"));
    cout << *p1 << endl;

    auto_ptr<string> p2;
    p2 = p1;
    cout << *p2 << endl;

    // cout << *p1 << endl;    //此时再次访问p1会报错
}
2、unique_ptr:

unique_ptr 实现独占式拥有,保证同一时间内只有一个智能指针可以指向该对象。也是采用所有权模式,但它可以避免内存空间泄漏,因为它针对于(new创建的对象,忘记delete)在编译期就会直接报错,不会在运行期埋下隐患。

void testUniquePtrFunc()
{
    unique_ptr<string> p1 (new string("This is a unique_ptr!"));
    cout << *p1 << endl;

    unique_ptr<string> p2;
    // p2 = p1;//编译期直接报错
    p2 = move(p1);//这样才能赋值,所有权转移

    cout << "move p2:" << *p2 << endl;

    p1 = unique_ptr<string> (new string("Assignment new value!"));
    cout << "p1 new value:" << *p1 << endl;
    cout << "p2 new value:" << *p2 << endl;

    unique_ptr<string> p3;
    p3 = unique_ptr<string> (new string("Should assignment value!"));
    cout << *p3 << endl;

}
3、shared_ptr:

shared_ptr 实现共享式拥有模式。多个智能指针可以指向相同的对象,此对象会与其相关资源在最后一个引用被销毁后释放。它使用计数机制来表示资源被几个指针共享。shared_ptr是为了解决auto_ptr在对象所有权上的局限性,使用引用计数的机制来实现共享所有权的智能指针。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁保护。

(1)、成员函数

a)、use_count:返回引用计数的个数;
b)、unique:返回是否是独占所有权(use_count 为1);
c)、swap:交换两个shared_ptr对象;
d)、reset:放弃内部对象的所有权或拥有对象的变更,会引起原有对象的引用计数的减少;
e)、get:返回内部对象(指针),由于已经重载()方法,因此和直接使用对象是一样的。

void testSharedPtrFunc()
{
    string *str = new string("This is String!");

    shared_ptr<string> p1(str);

    shared_ptr<string> p2;
    p2 = p1;

    cout << "p1 use_count:" << p1.use_count() << endl;
    cout << "p2 use_count:" << p2.use_count() << endl;
    cout << "p1 所有权的值:" << p1.unique() << endl;

    string *str3 = new string("This is String three!");

    shared_ptr<string> p3(str3);

    cout << "p1 swap before:" << p1.get() << endl;
    cout << "p3 swap before:" << p3.get() << endl;
    swap(p1, p3);
    cout << "p1 swap after:" << p1.get() << endl;
    cout << "p3 swap after:" << p3.get() << endl;

    cout << "after p1 use_count:" << p1.use_count() << endl;
    cout << "after p2 use_count:" << p2.use_count() << endl;

    cout << "before set value p1:" << p1.get() << endl;
    cout << "before set value p2:" << p2.get() << endl;
    p2 = p1;
    cout << "after set value p1:" << p1.get() << endl;
    cout << "after set value p2:" << p2.get() << endl;

    cout << "after new p1 use_count:" << p1.use_count() << endl;
    cout << "after new p2 use_count:" << p2.use_count() << endl;

    p1.reset();

    cout << "reset p1 use_count:" << p1.use_count() << endl;
    cout << "reset p2 use_count:" << p2.use_count() << endl;
}

4、weak_ptr:

weak_ptr是一种不控制对象生命周期的智能指针(即弱引用,不强持有对象,引用计数不会增加),它指向一个shared_ptr管理的对象。weak_ptr的目的是打破shared_ptr引起的循环引用,导致内存泄漏的问题。因为两个shared_ptr的对象,相互引用了,那么这两个指针的引用计数永远不会为0,从而内存资源也得不到释放,因此就造成了内存泄漏的风险。

(1)、weak_ptr 与 shared_ptr 之间相互转化

由于不能通过weak_ptr直接访问对象的方法,因此需要将它转化为shared_ptr才能访问对象的方法。weak_ptr没有重载*和->,但可以使用lock获取一个可用的shared_ptr对象。weak_ptr支持拷贝或赋值,但不会影响对应的shared_ptr内部对象的引用计数。
注意:weak_ptr在使用前需要检查其合法性(判断是否已经被释放了)。
expired():用于检查所管理的对象是否已经释放,若已释放,则返回true,否则返回false;
use_count():返回与shared_ptr共享的对象的引用计数;
reset():将weak_ptr置空。

class B;
class A
{
public:
    //这样会造成循环引用,导致内存泄漏
    // shared_ptr<B> _pb;
    //使用weak_ptr 弱引用可以打破循环引用,避免内存泄漏
    weak_ptr<B> _pb;

    ~A()
    {
        cout << "A delete \n";
    }
};

class B
{
public:
    shared_ptr<A> _pa;

    ~B()
    {
        cout << "B delete \n";
    }

    void print()
    {
        cout << "This is Print!" << endl;
    }

};
//MARK: -- Test weak_ptr function
void testWeakPtrFunc()
{
    shared_ptr<B> pb(new B());
    shared_ptr<A> pa(new A());

    cout << "pb use_count:" << pb.use_count() << endl;
    cout << "pa use_count:" << pa.use_count() << endl;
    
    pb->_pa = pa;
    pa->_pb = pb;

    cout << "after pb use_count:" << pb.use_count() << endl;
    cout << "after pa use_count:" << pa.use_count() << endl;

    //weak_ptr 访问对象的方法
    //先检查weak_ptr的合法性
    if (!pa->_pb.expired())
    {
        // pa->_pb->print();   //编译报错

        shared_ptr<B> tmpP = pa->_pb.lock();
        tmpP->print();
    }
}
5、shared_ptr 和 weak_ptr的实现原理:
/*
    shared_ptr 和 weak_ptr的实现原理
*/
//MARK: -- Counter 简单实现
class Counter
{
public:
    Counter() : s(0), w(0){};
    int s;   //shared_ptr 的引用计数
    int w;   //weak_ptr 的引用计数
};

//MARK: -- shared_ptr 的简单实现
template <class T>
class WeakPtr;   //为了用weak_ptr的lock(), 来生成shared_ptr使用,需要拷贝构造

template <class T>
class SharedPtr
{
public:
    SharedPtr(T *p = 0) : _ptr(p)
    {
        cnt = new Counter();

        if (p)
        {
            cnt->s = 1;
        }

        cout << "In construct" << cnt->s << endl;
    }

    ~SharedPtr()
    {
        release();
    }

    SharedPtr(SharedPtr<T> const &s)
    {
        cout << "In s copy con" << endl;
        _ptr = s._ptr;
        s.cnt->s++;
        cout << "Copy s construct" << s.cnt->s << endl;

        cnt = s.cnt;
    }

    SharedPtr(WeakPtr<T> const &w)
    {
        cout << "In w copy con" << endl;
        _ptr = w._ptr;
        w.cnt->s++;
        cout << "Copy w construct" << w.cnt->s << endl;

        cnt = w.cnt;
    }

    SharedPtr<T> &operator=(SharedPtr<T> &s)
    {
        if (this != &s)
        {
            release();
            s.cnt->s++;
            cout << "assign construct" << s.cnt->s << endl;
            cnt = s.cnt;
            _ptr = s._ptr;
        }

        return *this;
    }

    T &operator*()
    {
        return *_ptr;
    }

    T &operator->()
    {
        return _ptr;
    }

    friend class WeakPtr<T>;   //方便weak_ptr 与 shared_ptr设置引用计数和赋值

protected:
    void release()
    {
        cnt->s--;
        cout << "Release" << cnt->s << endl;
        if (cnt->s < 1)
        {
            delete _ptr;
            if (cnt->w < 1)
            {
                delete cnt;
                cnt = nullptr;
            }
        }
    }

private:
    T *_ptr;
    Counter *cnt;
};

//MARK: -- weak_ptr 简单实现
template <class T>
class WeakPtr
{
public:
    WeakPtr()
    {
        _ptr = 0;
        cnt = 0;
    }

    WeakPtr(SharedPtr<T> &s) : _ptr(s._ptr), cnt(s.cnt)
    {
        cout << "w con s" << endl;
        cnt->w++;
    }

    ~WeakPtr()
    {
        release();
    }

    WeakPtr<T> &operator = (WeakPtr<T> &w)
    {
        if (this != &w)
        {
            release();
            cnt = w.cnt;
            cnt->w++;
            _ptr = w._ptr;
        }

        return *this;
    }

    WeakPtr<T> &operator = (SharedPtr<T> &s)
    {
        cout << "w = s" << endl;
        release();
        cnt = s.cnt;
        cnt->w++;
        _ptr = w._ptr;

        return *this;
    }

    SharedPtr<T> lock()
    {
        return SharedPtr<T>(*this);
    }

    bool expired()
    {
        if (cnt)
        {
            if (cnt->s > 0)
            {
                cout << "empty!" << cnt->s << endl;
                return false;
            }
        }

        return true;
    }

//方便weak_ptr与share_ptr设置引用计数和赋值
    friend class SharedPtr<T>;

protected:
    void release()
    {
        if (cnt)
        {
            cnt->w--;
            cout << "WeakPtr release" << cnt->w << endl;
            if (cnt->w < 1 && cnt->s < 1)
            {
                cnt = nullptr;
            }
        }
    }

private:
    T *_ptr;
    Counter *cnt;
};

参考文献:

https://www.cnblogs.com/WindSun/p/11444429.html

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

推荐阅读更多精彩内容

  • 12章之前的程序中使用的对象都有严格定义的生存期。 全局对象在程序启动时分配,在程序结束时销毁。 对于局部自动对象...
    Kreat阅读 418评论 0 0
  • 1、问题引入:Java和C#等语言有自己的垃圾回收机制,.net运行时和java虚拟机可以管理分配的堆内存,在失去...
    Nrocinu阅读 369评论 0 0
  • C++ 智能指针详解 一、简介由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 de...
    yangqi916阅读 1,360评论 0 2
  • C++里面的四个智能指针: auto_ptr, unique_ptr,shared_ptr, weak_ptr 其...
    贰爷阅读 200评论 0 2
  • 0、摘要 本文先讲了智能指针存在之前C++面临的窘境,并顺理成章地引出利用RAII技术封装普通指针从而诞生了智能指...
    嗯哼_9793阅读 738评论 0 0