龙岗建网站公司/沧州网络推广公司
本小节学习的知识点分别是智能指针(unique_ptr)之详述、删除器、尺寸以及智能指针总结。
今天总结的知识分为以下4个大点:
(1)返回unique_ptr
(2)指定删除器
(3)尺寸问题
(4)智能指针总结
(4.1)智能指针背后的设计思想
(4.2)auto_ptr(C++98)为什么被废弃
(4.3)智能指针的选择问题
(1)返回unique_ptr:
我们知道,unique_ptr<T>是一种独占式的智能指针,不允许拷贝/赋值给其他unique_ptr<T>指针!但是,当unique_ptr<T>作为一个临时对象指针时,就可以做拷贝和赋值操作了!
auto mfunc(int val) {return unique_ptr<int>(new int(val));//返回unique_ptr<int>这个临时指针对象
}
unique_ptr<int> ptr1(unique_ptr<int>(new int()));//临时的unique_ptr对象作拷贝操作✓!
unique_ptr<int> ptr2 = mfunc(188);//临时的unique_ptr对象作赋值操作✓!
(以后coding时看到别人写这种返回unique_ptr指针的用法千万不要以为是错的!)
(2)指定删除器:
指定unique_ptr智能指针的删除器的格式:
unique_ptr<所指向的varType,deleterTypeName> 智能指针变量名;
<内>,先指定unique_ptr指针所指向的对象的类型名,再指定删除器的类型名(注意:这里的类型名,就是函数类型的删除器,你要用typedef来定义一个指向该类型函数的一个函数指针类型,然后再将该函数指针类型传入unique_ptr指针中!)
例子代码:
void mydeleter(int* pval) {delete pval;pval = nullptr;cout << "调用了自己指定的函数类型的删除器!" << endl;
}
//a)函数类型的删除器(用法介绍):
//a.1):
typedef void (*funcPointer)(int*);
//定义了一种函数指针类型funcPointer,这种类型的指针是指向一个函数返回值为void,参数类型为int*的函数的!
unique_ptr<int, funcPointer> ptr1(new int(18), mydeleter);
//a.2):
using fp = void(*)(int*);
//定义了一种函数指针类型fp,这种类型的指针是指向一个函数返回值为void,参数类型为int*的函数的!
unique_ptr2<int, fp> ptr2(new int(28), mydeleter);
//a.3):
typedef decltype(mydeleter)* fpp;
//因为decltype关键字返回的是一种函数类型void (int*)
//加上*号就让fpp == void (*)(int*) 这种函数指针类型
//定义了一种函数指针类型fpp,这种类型的指针是指向一个函数返回值为void,参数类型为int*的函数的!
unique_ptr<int, fpp> ptr3(new int(38), mydeleter);
//a.4):
unique_ptr<int, decltype(mydeleter)*> ptr4(new int(48), mydeleter);
//a.5):
//用lambda表达式(函数对象)来do
auto mydeleter2 = [](int* pval) {delete pval;pval = nullptr;cout << "调用了自己指定的函数类型的删除器!" << endl;
};
unique_ptr<int, decltype(mydeleter2)> ptr5(new int(58), mydeleter2);
运行结果都是:
指定删除器的补充说明:
在之前的学习中,我们都知道,即便2个shared_ptr的指定删除器类型名不同,但是varType一样的话,那么这2个shared_ptr也会算作是同一类型的指针!
比如:
void myIntDelete(int* pt) {delete pt;pt = nullptr;
}
shared_ptr<int> sp1(new int(), default_delete<int>());
shared_ptr<int> sp2(new int(),myIntDelete);
可认为sp1和sp2是同一种类型的shared_ptr指针
但是,unique_ptr则与shared_ptr不同!因为对于unique_ptr<T,deleter>模板类指针来说,其所指向对象的类型T和所使用的删除器deleter都属于该类指针的类型名!
比如:
typedef void (*funcPointer)(int*);
unique_ptr<int, funcPointer> unptr1(new int(18), mydeleter);
//用lambda表达式(可以理解为一个类class)来do
auto mydeleter2 = [](int* pval) {delete pval;pval = nullptr;cout << "调用了自己指定的函数类型的删除器!" << endl;
};
unique_ptr<int, decltype(mydeleter2)> unptr2(new int(28), mydeleter2);
不可认为unptr1和unptr2是同一种类型的unique_ptr指针
(3)尺寸问题:
unique_ptr智能指针占用的内存一般都会与裸指针所占用的内存是保持一致的!
int a = 1;
int* pt = &a;
int len1 = sizeof(pt);//8字节 与int*的裸指针一样大!
unique_ptr<int> unptr(new int(1));
int len2 = sizeof(unptr);//8字节 与int*的裸指针一样大!
特殊case:当unique_ptr智能指针带有自己指定的删除器deleter时,其所占据的内存空间就和裸指针的就有可能不一致了!
与对应裸指针所占据的内存不一致的case:
void mydeleter(int* pval) {delete pval;pval = nullptr;cout << "调用了自己指定的函数类型的删除器!" << endl;
}
typedef void (*fp)(int*);
unique_ptr<int, fp> unptr2(new int(2), mydeleter);
int len3 = sizeof(unptr2);//16字节 与int*的裸指针不一样大!
指定了删除器deleter后与对应裸指针所占据的内存还保持一致的case:(使用lambda表达式作为unique_ptr指针的deleter)
auto mydeleter2 = [](int* pval) {delete pval;pval = nullptr;cout << "调用了自己指定的函数类型的删除器!" << endl;
};
unique_ptr<int, decltype(mydeleter2)> unptr(new int(123), mydeleter2);
int len = sizeof(unptr);//8字节 与int*的裸指针一样大!
但是对于shared_ptr这种智能指针而言,不论你指定何种deleter,shared_ptr的尺寸(所占据的内存空间的大小)都是裸指针的2倍!
比如:
auto mydeleter2 = [](int* pval) {delete pval;pval = nullptr;cout << "调用了自己指定的函数类型的删除器!" << endl;
};
shared_ptr<int> sp1(new int(1), mydeleter2);
int len1 = sizeof(sp1);
shared_ptr<int> sp2(new int(2), default_delete<int>());
int len2 = sizeof(sp1);
shared_ptr<int> sp3(new int(3));
int len3 = sizeof(sp3);
cout << "len1 = " << len1 <<" len2 = "<< len2 << " len3 = " << len3 << endl;
//result: len1 == len2 == len3 == 16字节 == 裸指针int* 所占据的内存大小8字节的2倍
从上述代码的运行结果可见,对于unique_ptr智能指针,自己指定deleter时,又可能会导致增加字节数的情况发生,而字节数的增加又会影响你代码的效率!
结论:对于unique_ptr智能指针自定义的deleter请务必要谨慎使用!
(4)智能指针总结:
(4.1)智能指针背后的设计思想:
我们此前详细地学习了很多关于几种智能指针的用法。但是我们务必要清楚,到底为什么要引入智能指针呢?(面试考)这就是问你智能指针背后都思想到底是啥呢?
答:为了防止我们程序员忘记释放内存,从而导致内存泄露leakage问题,进而导致你的代码崩溃,so出现了智能指针!
(4.2)auto_ptr(C++98)为什么被废弃:
auto_ptr和unique_ptr一样,也是独占式的智能指针,但是C++11已经强烈表示不建议再使用这种不好的老智能指针了。原因如下:
reason1:auto不能保存到容器(比如vector/string)中
reason2:也不能从函数中返回auto_ptr指针
reason3:用法有缺陷(陷阱)
例子代码:
auto_ptr<int> ptr1(new int(1));
auto_ptr<int> ptr2(ptr1);
调试运行结果:
运行auto_ptr<int> ptr2(ptr1);这行代码之前的调试结果:
运行auto_ptr<int> ptr2(ptr1);这行代码之后的调试结果:
从运行结果可见,一旦你将一个auto_ptr赋值/拷贝给另一个auto_ptr时,就会出现原auto_ptr被置为nullptr的情况!这样你在后序coding时若不注意就一定会写出错误的代码!这即是auto_ptr被C++11的新标准弃用的一个重要reason!
auto_ptr is a poor/bad choice!
(4.3)智能指针的选择问题:
a)当你的程序中需要使用多个指向同一个对象的指针,则首选shared_ptr;
b)当你的程序中只需要使用指向一个对象的指针,则首选unique_ptr;(实际开发中,常用)
好,以上就是我总结的关于所有的智能指针知识点的笔记。希望你能读懂并且消化完,也希望自己能牢记这些小小的细节知识点,加油吧,我们都在coding的路上~