C++_chapter12_C++智能指针
C++中的智能指针,使用了RAII特性,用对象的生命周期管理资源生命周期。进入作用域构造、离开作用域析构,确保异常/早退路径也能释放资源。在创建智能指针时候,第二个参数可以指定自定义删除器。下面代码,当p291指向对象被析构时候,调用myDelete释放内存。注意:自定义删除器有一个参数,参数是一个裸指针,类型与智能指针类型相同。// 自定义删除器用于删除指针,当智能指针引用计数变为0时,就会自动
本章记录C++的智能指针,shared_ptr,unique_ptr 和 weak_ptr
文章目录
- 第12章 智能指针
-
- 12.1 直接内存管理(new.delete)、观察内存泄漏
- 12.2 智能指针概述
- 12.3 shared_ptr
- 12.4 weakptr
- 12.5 unique_ptr
- 12.6 智能指针总结
第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。
更多推荐



所有评论(0)