unique_ptr常用操作、删除器、尺寸[转]
目录
unique_ptr概述
- 独占式的概念(专属所有权):同一个时刻,只能有一个unique_ptr指针指向这个对象(这块内存);
- 当这个unique_ptr被销毁的时候,它所指向的对象也被销毁;
- 格式:
unique_ptr<指向的对象类型> 智能指针变量名:
常规初始化
unique_ptr和new配合
unique_ptr<int> pi; //可以指向int对象的一个空智能指针
if (pi == nullptr){
cout << "pi目前还是空指针" << endl;
}
unique_ptr<int> pi2(new int(105)); // 此时pi2指向一个值为105的int对象;
make_unique 函数
- C++ 11中没有,C++ 14才有的make_unique:不支持指定的删除器语法,如果不用删除器,建议优先选择使用,更高的性能。
unique_ptr<int> p1 = make_unique<int>(100);
auto p2 = make_unique<int>(200);
unique_ptr<int> pi22(new int(105)); //前边不能简写为auto;
unique_ptr常用操作
unique_ptr不支持的操作
unique_ptr<string> ps1(new string("I Love China!"));
unique_ptr<string> ps2(ps1); //该智能指针不支持拷贝动作(定义时初始化)
unique_ptr<string> ps3 = ps1; //该智能指针不支持拷贝动作(定义时初始化)
unique_ptr<string> ps4;
ps4 = ps1; //ps1是独占式智能指针,独占式智能指针不支持赋值操作
移动语义
unique_ptr<string> ps21(new string("I Love China!"));
unique_ptr<string> ps22 = std::move(ps21); // 移动完后,ps21 位空, ps22指向原来ps21所指
release():
- 放弃对指针的控制权(切断了智能指针和其所指向的对象之间的联系)。
- 返回裸指针,将该智能指针置空。
- 返回的这个裸指针我们可以手工delete来释放,也可以用来初始化另外一个智能指针,或者给另外一个智能指针赋值。
unique_ptr<string> ps31(new string("I Love China!"));
unique_ptr<string> ps32(ps31.release());
if (ps31 == nullptr){
cout << "ps31 被置空" << endl;
}
//ps32.release(); // 导致内存泄漏
string * tempp = ps32.release(); // auto tempp =
//delete tempp; //人工(手工)delete释放
reset()
- reset() 不带参数情况: 释放智能指针所指向的对象,并将智能指针置空。
unique_ptr<string> ps41(new string("I Love China!"));
ps41.reset();
if (ps41 == nullptr) {
cout << "ps41 被置空" << endl;
}
- reset()带参数的情况:释放 智能指针所指向的对象,并让该智能指针指向新对象。
unique_ptr<string> ps42(new string("I Love China!"));
ps41.reset(ps42.release());
// reset释放ps41指向的对象内存,让ps41指向ps42所指向的内存,同时ps42被置空
ps41.reset(new string("I Love China!"));
= nullptr
- 释放智能指针所指向的对象,并将智能指针置空。
unique_ptr<string> ps51(new string("I Love China!"));
ps51 = nullptr; // 释放ps51所指向的对象,并将ps51置空
指向一个数组
unique_ptr<int[]> ptrarray(new int[10]); // 注意,数组这里要跟上[]
ptrarray[0] = 12;
ptrarray[1] = 24;
//这里不要忘记A[] ,否则如果有自己的析构函数,则会报异常
unique_ptr<A[]> ptrarray1(new A[100]);
get()
- 返回智能指针中保存的裸指针。
- 考虑到有些函数参数需要的是内置裸指针(第三方库,咱们更改不了代码),所以引入该函数;
unique_ptr<string> ps71(new string("I Love China!"));
string* ps7 = ps71.get();
*ps7 = "This is a test!";
//delete ps7; // 不要这么干,否则产生不可预料的后果;
*解引用
- 获取该智能指针指向的对象,可以直接操作:
unique_ptr<string> ps81(new string("I Love China!"));
ps7 = ps81.get();
unique_ptr<int> pt1(new int(100));
*pt1 = 200;
unique_ptr<int[]> pt2(new int[10]); //对于定义的内容是数组,是没有*解引用运算符的;
swap():
- 交换两个智能指针所指向的对象。
unique_ptr<string> ps91(new string("I Love China1!"));
unique_ptr<string> ps92(new string("I Love China2!"));
std::swap(ps91, ps92);
ps91.swap(ps92);
智能指针名字作为判断条件
unique_ptr<string> ps101(new string("I Love China1!"));
if (ps101){
//若ps101 指向一个对象,那么这个就成立
cout << "ps101 不为空 " << endl;
}
ps101.reset();
if (ps101) {
cout << "ps101 不为空 " << endl;
}
else{
cout << "ps101 为空 " << endl;
}
转换成shared_ptr类型
- 如果unique_ptr为右值,就可以将它赋值给shared_ptr。
- 因为shared_ptr包含一个显示构造函数,可用于将右值unique_ptr转换为shared_ptr, shared_ptr将接管原来归unique_ptr所拥有的对象。
auto myfunc(){
return unique_ptr<string>(new string("I Love China!"));
// 这是个右值; (临时对象都是右值)
}
shared_ptr<string> pss1 = myfunc(); //这里会创建控制块
unique_ptr<string> ps11(new string("I Love China!"));
shared_ptr<string> pss2 = std::move(pss1);
// 左值转右值,执行后ps11为空, pss2就是shared_ptr
返回unique_ptr
- 虽然unique_ptr智能指针不能拷贝,但是,当这个unique_ptr将要被销毁,是可以拷贝的。最常见用法就是从函数返回一个unique_ptr。
unique_ptr<string> tuniqp(){
//unique_ptr<string> pr(new string("I Love China!"));
//return pr; //从函数返回一个局部的unique_ptr对象。
//返回这种局部对象,导致系统给我们生成一个临时unique_ptr对象,调用unique_ptr的移动构造函数。
return unique_ptr<string>(new string("I Love China!"));
}
unique_ptr<string> ps;
ps = tuniqp();
//可以用ps来接,则临时对象直接构造在ps里,
//如果不接,则临时对象会被释放,同时会释放掉所指向的对象的内存;
指定删除器
- delete默认删除器;
- a). 指定删除器
- 格式: unique_ptr<指向的对象类型, 删除器> 智能指针变量名;
- 删除器,可调用对象,比如函数,类重载了()。
- 我们学习过了shared_ptr 删除器,比较简单 shared_ptr<int> p(new int(), mydelete);unique_ptr删除器相对复杂一点,多了一步,先要在类型模板参数中传递进去类型名,然后在参数中再给具体的删除其函数名
- 不同方式的删除器
typedef void(*fp)(string *); // 定义一个函数指针类型,类型名为fp
unique_ptr<string, fp> ps1(new string("I Love China!"), mydeleter);
using fp2 = void(*)(string *); // 用using定义一个函数指针类型,类型名为fp2
unique_ptr<string, fp2> ps2(new string("I Love China!"), mydeleter);
typedef decltype(mydeleter)* fp3; //这里多了一个*, 因为decltype返回的是函数类型void (string*)
// 加* 表示函数指针类型,现在fp3 应该void *(string *);
unique_ptr<string, fp3> ps3(new string("I Love China!"), mydeleter);
unique_ptr<string, decltype(mydeleter)*> ps41(new string("I Love China!"), mydeleter);
//用lambda 表达式看看写法,lambda 表达式可以理解成带有operator()类类型对象。
//所以 decltype(mydella) = class{ ... }
auto mydella = [](string * pdel) { // 把lambda表达式理解成一个class;
delete pdel;
pdel = nullptr;
//可以打印日志
};
unique_ptr<string, decltype(mydella)> ps5(new string("I Love China!"), mydella);
指定删除器额外说明
- shared_ptr : 就算两个shared_ptr指定的删除器不相同,只要他们所指向的对象相同,那么这两个shared_ptr也属于同一个类型。
- 但是unique_ptr不一样,指定unique_ptr中的删除器会影响unique_ptr的类型,所以从灵活性来讲,shared_ptr设计的更灵活;
- 咱们在讲解shared_ptr的时候,删除器不同,但指向类型一样的shared_ptr,可以放到统一个容器里,vector<shared_ptr ...>
- unique_ptr如果删除器不同,那么就等于整个unique_ptr类型不同,这种类型不同的unique_ptr智能指针是没有办法放到同一个容器里的;
尺寸问题
- 通常情况下,unique_ptr 尺寸跟裸指针一样:
string *p;
int ilenp = sizeof(p); //4字节
unique_ptr<string> ps31(new string("I Love China!"));
int ilen = sizeof(ps31); //4字节
- 如果你增加了自己的删除器,则unique_ptr的尺寸可能增加,也可能不增加。
- 如果lambda表达式这种删除器,尺寸就没变化
- 定义一个函数作为删除器,尺寸发生变化,已经是8字节了。
- 增加字节对效率有影响,所以自定义删除器要慎用;
- shared_ptr, 不管你指定什么删除器, shared_ptr的尺寸(大小)都是裸指针的2倍;
智能指针总结
- 智能指针背后的设计思想
- 智能指针主要目的:帮助我们释放内存,以防止我们忘记释放内存时造成的内存泄漏。
- auto_ptr为什么被废弃
- auto_ptr : C++ 98时代的智能指针,具有 unique_ptr一部分特性; unique_ptr, shared_ptr,weak_ptr;
- 不能在容器中保存,也不能从函数中返回auto_ptr;
auto_ptr<string> ps4(new string("I Love China"));
auto_ptr<string> ps5 = ps4;// ps5指向字符串,ps4变成空了,这可以防止ps4和ps5析构一个string两次;
//用ps4(你没有意识到ps4已经空了),代码就会崩溃;
//这个也是auto_ptr用法上的一个陷阱。
shared_ptr<string> ps6(new string("I Love China"));
shared_ptr<string> ps7 = ps6; //ps7 和ps6都有效,强引用计数为2;
unique_ptr<string> ps8(new string("I Love China"));
unique_ptr<string> ps9 = ps8; //编译出错
//虽然auto_ptr 和unique_ptr都是独占式的,但unique_ptr 这种情况,编译的时候就会报错;
//而不会默默的把ps7的所有权转移到ps8上,避免后续如果使用ps7导致程序崩溃的问题;
//当然如果你用移动语义,也能达到auto_ptr的效果:
shared_ptr<string> ps10(new string("I Love China"));
shared_ptr<string> ps11 = std::move(ps10); //运用了移动语义
- auto_ptr被废弃的主要原因:设计的不太好,容易被误用引起潜在的程序崩溃等问题,所以C++ 11启用了unique_ptr来取代auto_ptr
- C++ 11表示不建议再使用auto_ptr ,强烈建议大家,用unique_ptr取代;unique_ptr比auto_ptr更安全;
- 智能指针的选择
- a). 如果程序要使用多个指向同一个对象的指针,应该选择shared_ptr;
- b). 如果程序不需要多个指向同一个对象的指针,应该首选unique_ptr;