本章记录C++的智能指针,shared_ptr,unique_ptr 和 weak_ptr

文章目录

第12章 智能指针

12.1 直接内存管理(new.delete)、观察内存泄漏

12.1.1 直接内存管理

12.1.1.1. 静态分配方式,直接分配在栈中
	int a = 10;				// 栈空间,自动释放
12.1.1.2直接内存管理方式:new
	int *p = new int;		 // 基本数据类型,没有指定初值,初值为随机值。
	int *p2 = new int(100);  // new int指定初始值。
	string *mystr = new string(5, 'a');  // 为字符串分配5个 'a' 
	vector<int> *point = new vector<int>{ 1,2,3,4,5 };  // point指向了vector<int>数组,这个数组中有5个元素,

如过需要初始化,直接再后边加上()即可。
值初始化 对于int类型,值初始化为0

string *str2 = new string();  
int *p3 = new int(); // 值初始化方式,值为0
12.1.1.3 new 和 delete使用说明

new delete 成对使用,new一块内存只能delete一次。

1 释放栈资源

不能释放栈资源,否则出现内存错误。

    void test()
    {
        int i = 12;
        int *p4 = &i;
        delete p4;
    }

2 重复释放相同的堆资源
    void test2()
    {
        int* p5 = new int();
        int* p6 = p5;
        delete p5;
        delete p6;
    }

12.1.2 观察内存泄漏

新建MFC工程,在初始化函数中做如下操作,当关闭对话框之后,就会提示内存泄漏。
在这里插入图片描述

12.2 智能指针概述

C++中的智能指针,使用了RAII特性,用对象的生命周期管理资源生命周期。进入作用域构造、离开作用域析构,确保异常/早退路径也能释放资源。

12.2.1 普通的裸指针

	cout << "智能指针" << endl;
	int *p21 = new int();
	int *q21 = p21;
	int *r = q21;

上面的这三个指针为裸指针。

12.2.2 智能指针与裸指针关系

 a) auto_ptr(C++98 )
 b) unique_ptr(C++11)
 c) shared_ptr(C++11)
 d) weak_ptr(C++11)
 weak_ptr
weak_ptr辅助shared_ptr,重点是 掌握unique_ptr和 shared_ptr;
 shared_ptr:
共享式指针指多个指针指向同一个对象,最后一个指针被销毁的时候,这个对象会被销毁。
 unique_ptr
独占式指针,同一个时间内,只有一个指针能够指向该对象。目前auto_ptr已经被unique_ptr取代。
下面分别介绍shared_ptr,weak_ptr和unique_ptr 。

12.3 shared_ptr

shared_ptr 之间相互协作,多个shared_ptr指向同一个资源。shared_ptr有额外开销,适用于共享场景。
工作原理:引用计数,每个shared_ptr可指向相同内存,但是只有最后一个指向该内存的指针才会析构该对象。最后一个shared_ptr 在引用计数变为0时候,释放内存。

12.3.1 shared_ptr初始化方式

方式1:基本数据类型:int
shared_ptr<int> p31(new int(100));
shared_ptr<int> p32 = new int(100);  // 不被允许:因为智能指针是explicit,不可进行隐士转换,必须直接初始化。

方式2: 函数返回值作为智能指针
shared_ptr<int> makes(int val)
{
	return shared_ptr<int>(new int(val));
}
shared_ptr<int> ptr = makes(5);

方式3:裸指针初始化shared_ptr (不推荐)
	int *pi2 = new int;
	shared_ptr<int> pl(pi2);

方式4: make_shared函数

make_shared函数能够在动态库中分配并初始化一个对象,然后返回指向该对象的shared_ptr指针。

shared_ptr<int> p41 = make_shared<int>(100);
类似于:int *ptr = new int(100);
int *p2 = new int(100);

shared_ptr<string> p3 = make_shared<string>(4,'g');
相当于 string *p3 = new string(4,'g');

shared_ptr<int> p42 = make_shared<int>(); // p42指向一个int,int里保存的值是0;
p42 = make_shared<int>(400); // p42指向一个新int,int里保存的是400,p42先释放掉指向的值,然后再指向新内存。

auto p43 = make_shared<string>();
// 自动类型推到。

12.3.2 shared_ptr引用计数增加和减少

12.3.2.1 shared_ptr 引用计数的增加

shared_ptr指向的对象的引用计数,可以通过3种方式增加.

方式1:shared_ptr新指针指向已构造的对象
auto p11 = make_shared<int>(100);  // p11指向的对象只有p11一个引用者。
auto p12 = p11;		// 智能指针定义时候的初始化,p11 p12指向对象有两个引用者。

在这里插入图片描述

方式2: 将智能指针当做实参往函数里传递

值传递: 当shared_ptr作为函数参数,进行值传递时,引用计数+1,但是函数执行完毕后,智能指针析构,引用计数恢复到原来的数量。
引用传递:引用计数不会增加。

    // 值传递时候,智能指针引用计数+1,当出了函数作用域后,
    // 又退出了。
    void myfunc2(shared_ptr<int> ptmp)
    {
        return;
    }

    void myfunc(shared_ptr<int>& ptmp)
    {
        return;
    }


    void test()
    {
        std::shared_ptr<int> p1 = std::make_shared<int>(32);
        myfunc2(p1);
    }

方式3:智能指针作为返回值,引用计数+1
shared_ptr<int>  myfunc2(shared_ptr<int> ptmp)
{
	return ptmp;
}

auto p13 = myfunc2(p12); // 引用计数+1 

12.3.1.2 shared_ptr引用计数减少
方式1:将shared_ptr指向新内存

将已经指向其他内存的shared_ptr指向新内存,原来的对象引用计数-1.

p13 = make_shared<int>(200);  // p13指向新对象,计数为1,p13指向的对象计数恢复为2。
p12 = make_shared<int>(400);  // p12指向新对象,计数为1 
p11 = make_shared<int>(400);

方式2:函数体中临时的shared_ptr,出了作用域后-1

这种情况类似于上边的方式2。

方式3:引用计数减为0

当一个shared_ptr引用计数从1变为0,它会自动释放自己管理的对象

	auto p9 = make_shared<int>(100);
	auto p10 = make_shared<int>(100);
	 p9 = p10;

上边代码,自动释放p9内存。

12.3.3 shared_ptr 智能指针常用方法

总结shared_ptr常用的方法。

2.3.3.1 use_count()

使用use_count()来查看当前使用的内存情况。

cout << "2.1 use_count() 返回指向对象引用计数" << endl;
shared_ptr<int> myp1(new int(100));
auto myp2 = myp1;
int icount = myp2.use_count();
cout << icount << endl;

icount引用计数为2,myp1 和 myp2都是2.
在这里插入图片描述

2.3.3.2 unique()

unique() 检测是否该智能指针独占某个指向的对象,也就是若只有一个智能指针指向该对象,则unique() 返回true;
use_count() 不是1,则unique()是 false;

	shared_ptr<int> p22(new int(20));
	shared_ptr<int> p23(p22);
	if (p22.unique())
	{
		cout << "unique OK" << endl;
	}
	else
	{
		cout << "unique false" << endl;
	}

2.3.3.3 reset()

reset()是一个函数重载。

reset() 不带参数

 若 pi 是唯一指向该对象的指针,那么释放Pi所指向的对象,并将pi置空;
 若 pi 不是唯一指向该对象的指针,那么释放pi所指向的对象,但只限更改对象的引用计数会减少1,并将Pi置空。

reset() 带参数

 若pi是唯一指向该对象的指针,则释放pi指向的对象,让pi指向新内存。
 若pi不是唯一指向该对象的指针,则不释放Pi指向的对象,但指向该对象的引用计数会少1,同时让pi指向新对象。

    void test()
    {
        std::shared_ptr<int> pi(new int(2));
        auto pi2(pi);
        pi.reset(new int(1));

        int a;
    }

2.3.3.4 解引用:获取p指向的对象
	shared_ptr<int> pother(new int(1234));
	cout << *pother << endl;

2.3.3.5 get()返回p中保存的指针

获取智能指针对应的裸指针。

	shared_ptr<int> myp(new int(100));
	int *p = myp.get();

某些情况下,如果只能使用裸指针,那么就需要将智能指针,转为裸指针。

2.3.3.6 swap() 交换两个智能指针

交换两个指针指向的对象。

    void test()
    {
        shared_ptr<string> ps1(new string("l love China1"));
        shared_ptr<string> ps2(new string("l love China2"));
        cout << ps1->c_str() << endl;
        cout << ps2->c_str() << endl;
        std::swap(ps1, ps2);
        cout << "交换后" << endl;
        cout << ps1->c_str() << endl;
        cout << ps2->c_str() << endl;
        /*
            l love China1
            l love China2
            交换后
            l love China2
            l love China1
        */
    }

2.3.3.7 =nullptr

a, 将所指向的对象引用计数-1,若引用计数变为0,则释放智能指针所指向的对象。
b, 若指向的对象的引用计数大于1,则置空该指针,并将指向对应的引用计数-1.

	shared_ptr<string> ps3(new string("I Love China!"));
	ps1 = nullptr;

	shared_ptr<string> ps3(new string("I Love China!"));
	shared_ptr<string> ps4(ps3);
	ps3 = nullptr;
	if (ps3)
	{
		cout << "指向一个对象" << endl;
	}
	else
	{
		cout << "空" << endl;
	}

ps3 变为空,同时ps4引用计数-1.

2.3.3.8 智能指针名字作为判断条件
    void test()
    {
        // shared_ptr:共享所有权
        std::shared_ptr<std::string> sp;   // 空
        if (!sp) 
            std::cout << "sp 为空\n";
        // sp 为空

        sp = std::make_shared<std::string>("hello");
        if (sp)                            // 非空
            std::cout << "sp.size = " << sp->size() << "\n";
        // sp.size = 5
    }

2.3.3.9 指定删除器
方式1:自定义删除器

在创建智能指针时候,第二个参数可以指定自定义删除器。
下面代码,当p291指向对象被析构时候,调用myDelete释放内存。
注意:自定义删除器有一个参数,参数是一个裸指针,类型与智能指针类型相同。

    // 自定义删除器用于删除指针,当智能指针引用计数变为0时,就会自动调用该删除器来删除对象。
    void myDelete(int* p)  
    {
        // 写一些日志 
        cout << "自定义删除器" << endl;
        delete p;
    }

    void test()
    {
        shared_ptr<int> p1(new int(1234), myDelete);
        shared_ptr<int> p2(p1);
        p1.reset(); //  剩下一个引用计数
        p2.reset();  // 剩下一个引用计数,调用自定义删除器删除
        // 自定义删除器
    }

方式2:lambda表达式作为删除器

普通函数可以为一个函数对象传递;lambda表达式仍然是一个函数对象,所以lambda可以为之函数对象传递。

	shared_ptr<int> p293(new int(123), [](int *p) {
		delete p;
		cout << "lambda  自定义删除器" << endl;
	});		
	p293.reset();

方式3 :new的数组自定义删除器

reset() 释放内存时候,会调用lambda表达式删除器。

    class A
    {
    public:
        A()
        {
            cout << "构造被调用" << endl;
        }
        ~A()
        {
            cout << "析构被调用" << endl;
        }
    };

    void test()
    {
        shared_ptr<A> p(new A[10], [](A* p) {
            delete[]p;
            cout << "A数组被删除" << endl;
           });
        p.reset();

        // A数组,调用的10次构造函数
    }

方式4:C++17 解决默认不能删除数组对象问题

语法格式为:在<>指定类型后边,加上[],解决编译器默认情况下不能删除数组对象的问题。

	shared_ptr<A[]> Pa(new A[10]);

2.3.3 写模板函数来封装shared_ptr数组

// 写函数模板来封装 shared_ptr数组 
template<typename T>
shared_ptr<T> make_shared_array(size_t size)
{
	return shared_ptr<T>(new T[size], default_delete(T[])());
}

12.3.4 shared_ptr使用场景陷阱,性能分析,使用建议

12.3.4.1 shared_ptr使用场景

使用一个函数返回一个智能指针时,一定要用一个智能指针来接,否则直接就析构掉了。

    // 创建并返回一个指针指针
    shared_ptr<int> create(int value)
    {
        return make_shared<int>(value);
    }

    void func(int value)
    {
        shared_ptr<int> shp = create(value);  // 引用计数为1;
        return;
    }

    // 对其进行改造
    shared_ptr<int> func2(int value)
    {
        shared_ptr<int> shp = create(value);
        return shp;
    }


    void test()
    {
        func2(100);  // 如果没有智能指针来接这个返回结果,智能指针就会被释放;
        shared_ptr<int> p = func2(11);  // 不会被释放
        return;
    }

12.3.4.2 shared_ptr 使用陷阱分析
1 慎用裸指针

慎用裸指针与强指针关联(比如裸指针初始化强指针),然后再用裸指针释放这块内存。这将引用这块内存被释放两次。
下面的代码中,用裸指针初始化了临时对象,然后作为函数参数传入,等这个函数执行结束,会将p 指向的内存释放,然后再用p时候,就会引用一块非法内存。

    void proc(shared_ptr<int> ptr)
    {
        return;
    }

    void test()
    {
        int* p = new int(11);
        {
            proc(shared_ptr<int>(p));  // 参数是临时对象
        }

        *p = 222; //
        cout << "p = " << *p << endl;
        // 输出:222 

        // 分析:p被释放了,属于未定义行为,能打印是因为释放后的那块内存还没有被复用,被覆盖
        // 写入没有立即崩溃。
    }

2 慎用 get() 返回的指针 : 将强指针转为一个裸指针
	cout << "2.2  慎用 get() 返回的指针" << endl;
	shared_ptr<int> p26(new int(100));
	int *p = p26.get();   
	delete p; // 导致p26指向内容释放两次

注:不能用get得到的指针来初始化另一个智能指针。

3 不要把类对象初始化

不要把类对象指针(this)作为shared_ptr返回,改用 enable_shared_from_this<类名>
下面的错误相当于pct1 和 pct3指向了相同的对象,但是这两个指针指向了不同的控制块,导致了内存被释放后,pct3会再次释放内存。

    class CT
    {
    public:
        shared_ptr<CT> getself()
        {
            return shared_ptr<CT>(this);  // 用裸指针this 初始化了 临时对象 
        }
    private:

    };

    void test()
    {
        shared_ptr<CT> pct1(new CT);
        // shared_ptr<CT> pct2(pct1);  // 没问题

        shared_ptr<CT> pct3 = pct1->getself();  // 这相当于同一块内存被释放 两次,出现问题
    }

在这里插入图片描述
做如下修改,在类中继承如下代码中的类,返回shared_from_this() 可解决。

    class CT2 : public enable_shared_from_this<CT2>
    {
    public:
        shared_ptr<CT2> getself()
        {
            // return shared_ptr<CT>(this);  // 用裸指针this 初始化了 临时对象 
            return shared_from_this();
        }
    private:

    };


    void test()
    {
        shared_ptr<CT2> pct1(new CT2);
        // shared_ptr<CT> pct2(pct1);  // 没问题

        shared_ptr<CT2> pct3 = pct1->getself();  
    }

4 智能指针交叉使用

场景2:发布-订阅/观察者
订阅列表用 weak_ptr 存放订阅者,通知前 lock 一次,过期自动清理。

    struct Subject; // 这是主题 

    // 观察者 
    struct Observer 
    {
        std::weak_ptr<Subject> subject; // 只在需要时 lock,不延长被观察者寿命
        void onNotify() 
        { 
            std::cout << "Observer notified\n";
        }
        ~Observer() 
        { 
            std::cout << "~Observer\n"; 
        }
    };

    // 主题,负责维护订阅列表
    // 向订阅者发布通知
    struct Subject 
    {
        std::vector<std::weak_ptr<Observer>> observers; 

        // 关键:弱引用
        void subscribe(const std::shared_ptr<Observer>& ob) 
        { 
            observers.push_back(ob); 
        }

        void notify() 
        {
            for (auto it = observers.begin(); it != observers.end(); )
            {
                if (auto ob = it->lock()) // 如果有效 
                { 
                    ob->onNotify();
                    ++it; 
                }
                else
                {
                    it = observers.erase(it); // 自动清理已过期的订阅者
                }
            }
        }
        ~Subject()
        { 
            std::cout << "~Subject\n";
        }
    };

    void test()
    {
        auto s = std::make_shared<Subject>();
        auto o = std::make_shared<Observer>();
        o->subject = s;        // 观察者弱持有主体
        s->subscribe(o);       // 主体弱持有观察者
        s->notify();
        // 若两边都改成 shared_ptr:Subject 持有 Observer,Observer 再持有 Subject,会形成环 => 泄漏
    }

12.3.4.3控制块创建时机:两种方式
方式1:make_shared

make_shared:分配并初始化一个对象,返回指向此对象的shared_ptr,所以这个make_shared它总是能够创建一个控制块。

shared_ptr<int> p2 = make_shared<int>(100);

注意:make_shared()方式效率更高。

	shared_ptr<string> ps1(new string("aaa"));  // 分配两次内存
	auto ps2 = make_shared<string>("aaaa"); // 分配一次内存,性能高于上边用法

方式2:裸指针初始化shared_ptr
	int *pi = new int();
	shared_ptr<int> p2(pi);

12.4.4 移动语义 (让原来指针指向空)

	shared_ptr<int> p1(new int(100));  // 指向空
	shared_ptr<int> p2(std::move(p1)); // p2指向p1指向的内容

赋值要增加引用计数,移动只需要指针的赋值;

12.4 weakptr

2.4.1 weak_ptr 概述

主要作用:weak_ptr 辅助 shared_ptr工作,监视所指向对象是否存在。
weak_ptr的弱,其对应的强指针就是shared_ptr;
weak_ptr 也是个类模板,也是个智能指针。这个智能指针指向同一个shared_ptr管理的对象,但是weak_ptr不控制所指向对象的生命周期。
也就是说,weak_ptr绑定到shared_ptr上并不会改变shared_ptr引用计数,更确切的说,weak_ptr构造和析构并不影响所指向对象的引用计数。当shared_ptr需要释放的时候,不管是否有weak_ptr指向该对象,不影响释放shared_ptr指向的内存。
这个弱引用的作用:理解为监视shared_ptr 生命周期用的,是shared_ptr对所指向资源的扩展。weak_ptr不能独立操作指向的内存,需要用lock()转为强指针后,才可以转为弱指针。

1 weak_ptr使用

创建weak_ptr时候,一般是用一个shared_ptr(make_shared<>)来初始化;

	auto pi = make_shared<int>(100); // 先创建强指针
	weak_ptr<int> pw(pi);				// 然后用强指针给弱指针赋值

2 lock()功能

lock() 返回一个强智能指针。当弱指针想操作指向的内存的时候,就需要用lock()操作,返回一个强指针,用强指针操作这块内存。lock() 检查weak_ptr所指向的对象是否存在,如果存在,那么这个lock就能够返回一个指向该对象的shared_ptr;如果它所指向的对象不存在,lock会返回一个空的shared_ptr。

    void test()
    {
        shared_ptr<int> p1(new int(11));
        weak_ptr<int> p2 = p1;  // 用share_ptr 初始化 weak_ptr 
        p1.reset();

        auto pi2 = p2.lock();   // pi2 是一个shared_ptr 
        if (pi2 != nullptr)
        {
            *pi2 = 12;
        }
        else
        {
            cout << "指向空" << endl;
        }
        // 指向空
    }

2.4.2 weak_ptr常用操作

1 use_count

use_count 获取与该弱指针共享对象的其他shared_ptr数量,或者说获取所获资源的强引用计数。
强指针和弱指针都可使用use_count()方式,查看被指向对象的强指针数量。

	auto p21 = make_shared<int>(10);
	auto p22(p21);
	
	weak_ptr<int> p23 (p21);
	int count2 = p23.use_count();
	int count3 = p22.use_count();
	cout << count2 << endl;

2 expire()

是否过期意思,用来判断所观测的资源是否被释放。如果被释放了,返回true。

	auto p21 = make_shared<int>(10);
	// auto p22(p21);

	weak_ptr<int> p23(p21);
	p21.reset();  // 释放强指针指向资源。
	int count2 = p23.use_count();
	if (p23.expired())
	{
		cout << count2 << endl;
	}

3 reset()

将弱指针设置为空,不影响该对象引用数量,指向对象的弱引用数量。

2.4.3 weak_ptr和shared_ptr 内存模型

        this->_Ptr = _Px;
        this->_Rep = _Rx;

• _Ptr:指向被管理对象的裸指针(element_type*),也就是“数据指针”。
• _Rep:指向控制块(_Ref_count_base*),里面保存强引用计数、弱引用计数、删除器/分配器等元数据。
内存模型如下:
weak_ptr 和 shared_ptr 尺寸一样大,都是裸指针的两倍,他们内存都有两个指针。
第一个指针指向T类型;第二个指向控制块,内存模型如下:
在这里插入图片描述
控制块中有3部分:所指向对象的强引用计数;弱引用计数;其他数据。
第一个裸指针指向的是该智能指针所指向的对象;
第二个指向了一个很大的控制块,里边有:引用计数,弱引用计数 和 其他数据比如自定义删除器等。

12.5 unique_ptr

12.5.1 unique_ptr概述

独占式概念,同一时刻,只能一个指针指向某个对象,不允许其他指针该对象。
当unique_ptr被销毁时候,它所指向的对象也被销毁。

2.5.1.1常规初始化
	空指针:
	unique_ptr <int> pi;
	if (pi == nullptr)
	{
		cout << "pi目前还是空指针" << endl;
	}
	
	unique_ptr<int> pi2(new int(105));

2.5.1.2 make_unique函数

C++11中没有,C++17中才有; make_unique不支持删除器语法,如果不用删除器,建议优先选择使用,性能高。

	unique_ptr<int> p1 = make_unique<int>(100);
	auto p2 = make_unique<int>(100);

12.5.2 unique_ptr 常规操作

2.5.2.1 unique_ptr 不支持的操作拷贝,赋值
	unique_ptr<string> ps1(new string("I love you!"));
	unique_ptr<string> ps2(new string("I love you!"));
	ps1 = ps2; // 不允许

2.5.2.2 移动语义

只支持移动语义。

	unique_ptr<string> ps1(new string("I love you!"));

	unique_ptr<string> ps2 = std::move(ps1);

2.5.2.3 release()

放弃了对指针的控制权,切断了指针和所指向对象之间的联系。
返回指向的对象,可以用另一个指针来接。

unique_ptr<string > ps1(new string("I love China!"));
	unique_ptr<string> ps2(ps1.release());   // 将ps1切断,同时将ps1指向的资源给ps2. 
	if (ps1 == nullptr)
	{
		cout << "ps1 nullptr" << endl;;
	}
	cout << *ps2 << endl;

	// ps2.release();  //  这样导致内存泄漏,解决方法如下:手动释放
	string *tmp = ps2.release();
	delete tmp;

2.5.2.4 reset()

不带参数: 释放智能指针所指向内容,并将指针置空
带参数: 释放智能指针所指向对象,并让该智能指针指向新对象

	// 不带参数: 释放智能指针所指向内容,并将指针置空
	unique_ptr<string > ps1(new string("I love China!"));
	ps1.reset(); 

	// 带参数:释放智能指针所指向对象,并让该智能指针指向新对象
	unique_ptr<string > ps1(new string("I love China!"));
	ps1.reset(ps2.release());  // reset 释放ps1指向内存,然后ps1指向ps2执行内存,同时ps2置空

2.5.2.5 =nullptr

释放智能指针所指向对象,并将智能指针置空。

	unique_ptr<string> ps1(new string("L love China"));
	ps1 = nullptr;

2.5.2.6 指向一个数组

注意:数组时候,<>要跟上[]

	unique_ptr<int[]> ptarray(new int[10]); // 注意:数组时候,<>要跟上[] 
	// unique_ptr<int> ptarray(new int[10]); 如果不跟着[] 会报异常
	ptarray[0] = 1;
	ptarray[1] = 1;

2.5.2.7 get()返回智能指针中的裸指针
    void test()
    {
        std::unique_ptr<int> p1 = make_unique<int>(2);
        int* p = p1.get();
        cout << *p << endl;
        // 2 
    }

2.5.2.8获取该智能指针指向的对象,可以直接操作
    void test()
    {
        std::unique_ptr<int> p1 = make_unique<int>(2);
        int* p = p1.get();
        cout << *p << endl;
        // 2 

        // 直接使用 智能指针
        cout << *p1 << endl;
    }

2.5.2.9 swap()
        std::unique_ptr<int> p2 = std::move(p1);
        std::unique_ptr<int> p3 = make_unique<int>(4);
        p2.swap(p3);
        cout << *p2 << endl;
        // 4 
        cout << *p3 << endl;
        // 2
    }

2.6.2.10 智能指针名字作为判断条件
	unique_ptr<string> ps1(new string("L love China"));
	if (ps1)
	{
		cout << "not nullptr" << endl;
	}

2.6.2.11 unique_ptr转为 shared_ptr

如果 unique_ptr为右值,就可以将它赋值给shared_ptr。

	shared_ptr<int> ps = mufunc();
	// 创建了控制块
	unique_ptr<string> ps1(new string("L love China"));
	shared_ptr<string> ps2 = std::move(ps1);

2.6.2.12 函数返回unique_ptr

可以从函数中返回unique指针,然后用unique来接。

unique_ptr<string > ps = tunique();

12.5.3 指定删除器,delete

格式:unique_ptr<指定的对象类型,删除器> 智能指针变量名
unique_ptr<指定的对象类型,删除器> 智能指针变量名(new(), 删除器函数)

	有四种自定删除器的方式
    // 删除器1:
    // 删除器  string 类型 
    void mydelete(string* s)
    {
        delete s;
    }

    void test()
    {
        // 调用删除器 
        typedef void(*fp)(string*); // 定义一个函数指针类型,类型名为p
        unique_ptr<string, fp> ps22(new string("L love China"), mydelete);
        cout << sizeof(ps22) << endl;

        // 删除器2:using 方式定义函数类型
        using fp2 = void(*)(string*);  // 
        unique_ptr<string, fp2> ps23(new string("aaa"), mydelete);
        cout << sizeof(ps23) << endl;  // 8

        // 删除器3:
        typedef decltype(mydelete)* fp3;  // decltype返回的是函数类型  void (stirng *);  *fp3就是函数指针类型
        unique_ptr<string, fp3> ps24(new string("aaa"), mydelete);
        cout << sizeof(ps24) << endl;  // 8

        // 删除器4:
        // 使用Lambda表达式 看看写法,lambda可以看成是Operator()类型对象
        auto mydella = [](string* p)
        {
            delete p;
            p = nullptr;
        };
        // 获取到 decltype(mydella) 类型 
        unique_ptr<string, decltype(mydella)> ps25(new string("L love China"), mydella);
        cout << sizeof(ps25) << endl;
    }

12.5.4 unique_ptr尺寸问题

从上面的例子中可以看出,如果增加了自己的删除器,尺寸可能会增加,也可能不会。
a> 如果lambda作为删除器,unique_ptr尺寸不增加。
b> 使用函数指针作为删除器,unique_ptr 指针变为8字节

	string *p;
	int len1 = sizeof(p);
	unique_ptr<string> ps26(new string("aaa"));
	int len2 = sizeof(ps26); // 4字节
	cout << len2 << endl;

12.6 智能指针总结

1 智能指针背后设置思想:
智能指针主要目的:帮助我们释放内存,避免造成内存泄漏。

2 auto_ptr 为什么被抛弃
	  auto_ptr 被抛弃,不能在容器中保存,也不能从函数中返回auto_ptr;
	  auto_ptr 和 unique_ptr都是独占式的,但是unique_ptr这种情况,编译时候就会报错,而不会默默把ps所有权转移到ps2上;避免了后续再使用导致程序崩溃问题。

3 智能指针选择:如果多个指针要指向同一个对象,选择shared_ptr
多数情况下,使用unique_ptr。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐