C++中的智能指针
C++ 中的四个智能指针分别是
1.shared_ptr
2.unique_ptr
3.weak_ptr
4.auto_ptr(已经被c++11弃用)(在c++11的环境中会爆警告warning: 'auto_ptr' is deprecated)
智能指针的作用
智能指针的作用是管理一个指针。
因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。
使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
所以智能指针的设计思想简单的来说就是:将基本类型指针封装成类(模板类)对象指针,并且在析构函数里编写delete语句删除指针所指向的内存空间
智能指针的使用
智能指针所在的头文件:#include
- 智能指针都有一个explicit构造函数(显式构造函数)
templet
class auto_ptr{
explicit auto_ptr(X* p=0);
}
所以智能指针的调用都只能显示调用
eg:
#include
using namespace std;
int main(){
shared_ptr pd;
double *ptr_reg=new double;
pd=shared_ptr(ptr_reg);
shared_ptr pshared(ptr_reg);
}
经常容易犯错的地方:
#include
using namespace std;
int main(){
int a=5;
shared_ptr pt(&a);
}
这里我们定义的a用的是栈内存,在pt过期时,程序将把delete运算符用于非堆内存,这是错误的
对于两个指针指向同一个对象的问题,有如下解决办法
对于如下代码:
#include
using namespace std; int main(){ auto_ptr ps(new string ("first blood!")); auto_ptr tmp; tmp=ps; } 有两个指针指向“first blood”这一个string对象,那么程序会试图删除这个string对象两次,众所周知,指向一个已删除的对象的指针是野指针,这样会造成内存泄漏
为了避免野指针的出现,有如下解决方法
定义赋值运算符,使之执行深复制
什么是深复制:它除了会将原有对象的所有成员变量拷贝给新对象,还会为新对象再分配一块内存,并将原有对象所持有的内存也拷贝过来。这样做的结果是,原有对象和新对象所持有的动态内存是相互独立的,更改一个对象的数据不会影响另外一个对象,
这样执行的后果是浪费内存,智能指针未采用这个方案
建立所有权概念(auto_ptr、unique_ptr)
对于特定的对象,只有一个智能指针可以拥有,这样只有拥有特定对象的智能指针可以删除该对象。赋值操作等价于转让所有权。
引用计数法(shared_ptr)
跟踪特定对象的智能指针数,在赋值时,计数+1,过期时,计数-1,只有当引用计数减到0时才调用delete
为何弃用auto_ptr
安全问题,使用auto_ptr不当容易造成内存崩溃
eg:
#include
using namespace std; int main() { auto_ptr str[5] = { auto_ptr (new string("str1")), auto_ptr (new string("str2")), auto_ptr (new string("str3")), auto_ptr (new string("str4")), auto_ptr (new string("str5")), }; auto_ptr ps; ps = str[2]; for(int i = 0; i < 5; i++) { cout << *str[i] << endl; } } 运行改代码时,输出到str2时就程序停止运行了,这里是因为ps智能指针拿走了str[2]的所有权,在输出时访问空指针就会使得程序崩溃
而将auto_ptr换为shared_ptr或unique_ptr时,程序却不会崩溃
使用unique_ptr时,程序会报错:
note: declared here unique_ptr& operator=(const unique_ptr&) = delete;
所以auto_ptr就被弃用了23333
unique_ptr的优势
首先同样是所有权模式的智能指针,unique会在指针所有权被剥夺后再访问报错
其次,会及时销毁临时的指针
eg:
#include
using namespace std; unique_ptr get(const char *s) { unique_ptr tmp(new string(s)); return tmp; } int main() { unique_ptr ps; ps = get("23333"); cout<<*ps< 对于get函数返回的一个临时指针的所有权被ps指针拿到后,临时指针就被销毁了,这个临时指针不会访问无效数据,即unique_ptr (new Temp(val))可以作为一个临时右值出现
如果想将一个指针安全的赋值给另一个指针,可以使用c++标准库当中的move函数
eg:
#include
using namespace std; unique_ptr get(const char *s) { unique_ptr tmp(new string(s)); return tmp; } int main() { unique_ptr ps1, ps2; ps1 = get("str1"); ps2 = move(ps1); ps1 = get("str2"); cout << *ps2 << " " << *ps1 << endl; }
智能指针的选择
(1)如果程序要使用多个指向同一个对象的指针,应选择shared_ptr。这样的情况包括:
- 有一个指针数组,并使用一些辅助指针来标示特定的元素,如最大的元素和最小的元素;
- 两个对象包含都指向第三个对象的指针;
- STL容器包含指针。很多STL算法都支持复制和赋值操作,这些操作可用于shared_ptr,但不能用于unique_ptr(编译器发出warning)和auto_ptr(行为不确定)。如果你的编译器没有提供shared_ptr,可使用Boost库提供的shared_ptr。
(2)如果程序不需要多个指向同一个对象的指针,则可使用unique_ptr。
如果函数使用new分配内存,并返还指向该内存的指针,将其返回类型声明为unique_ptr是不错的选择。
这样,所有权转让给接受返回值的unique_ptr,而该智能指针将负责调用delete。可将unique_ptr存储到STL容器在那个,只要不调用将一个unique_ptr复制或赋给另一个算法(如sort())。
在unique_ptr为右值时,可将其赋给shared_ptr,这与将一个unique_ptr赋给一个需要满足的条件相同。
模板shared_ptr包含一个显式构造函数,可用于将右值unique_ptr转换为shared_ptr。shared_ptr将接管原来归unique_ptr所有的对象。
eg:
#include
using namespace std;
unique_ptr get_int(int n) {
return unique_ptr(new int(n));
}
int main() {
srand ( (unsigned int) time (NULL) );
unique_ptr up1(get_int(rand() % 100 + 1));
shared_ptr sp1(get_int(rand() % 10 + 1));
cout<<*up1<<" "<<*sp1<