c++11摒弃了auto_ptr,支持 shared_ptr,unique_ptr,weak_ptr,包含在头文件<memory>中。
智能指针是利用了一种叫做RAII(资源获取即初始化)的技术对普通的指针进行封装,用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。当超出了智能指针类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。
shared_ptr
shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每建一个shared_ptr,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。
- 初始化。智能指针是个模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用make_shared函数初始化。不能将指针直接赋值给一个智能指针,一个是类,一个是指针。例如std::shared_ptr<int> p4 = new int(1);的写法是错误的
- 拷贝和赋值。拷贝使得对象的引用计数增加1,赋值使得原对象引用计数减1,当计数为0时,自动释放内存。后来指向的对象引用计数加1,指向后来的对象。
- get函数获取原始指针,或者直接使用*或->使用原始指针。
- 注意不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存。
- 注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。循环引用可以使用weak_ptr避免。
#include <memory>
#include <iostream>
using namespace std;
int main()
{
int a = 5;
//shared_ptr<int> sp1(new int(5));
shared_ptr<int> sp1 = make_shared<int>(a);
cout<<"*sp1 = "<<*sp1<<endl;
cout<<"use count = "<<sp1.use_count()<<endl;
{
shared_ptr<int> sp2(sp1);
cout<<"*sp2 = "<<*sp2.get()<<endl;
cout<<"use count = "<<sp1.use_count()<<endl;
}
cout<<"use count = "<<sp1.use_count()<<endl;
return 0;
}
输出
*sp1 = 5
use count = 1
*sp2 = 5
use count = 2
use count = 1
unique_ptr
某个时刻只能有一个unique_ptr指向一个给定对象,由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作。 不可以对unique_ptr进行拷贝、赋值等操作,但是可以通过调用release或reset将指针所有权从一个(非const)unique_ptr转移给另一个unique。当unique_ptr被销毁时,该对象被销毁。
unique_ptr不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。要想创建一个unique_ptr,我们需要将一个new 操作符返回的指针传递给unique_ptr的构造函数。
#include <memory>
#include <iostream>
using namespace std;
int main()
{
unique_ptr<int> up1(new int(5));
cout<<"*up1 = "<<*up1<<endl;
cout<<"up1.get() = "<<up1.get()<<endl;
unique_ptr<int> up2 = move(up1);
cout<<"*up2 = "<<*up2<<endl;
cout<<"up2.get() = "<<up2.get()<<endl;
cout<<"up1.get() = "<<up1.get()<<endl;
return 0;
}
输出
*up1 = 5
up1.get() = 0xf71530
*up2 = 5
up2.get() = 0xf71530
up1.get() = 0
weak_ptr
weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,因为可能出现weak_ptr所指向的对象实际上已经被释放了的情况,所以weak_ptr有一个lock函数,尝试取回一个指向对象的shared_ptr。
#include <memory>
#include <iostream>
using namespace std;
class CB;
class CA
{
public:
shared_ptr<CB> pb_;
~CA()
{
cout<<"delete A"<<endl;
}
};
class CB
{
public:
shared_ptr<CA> pa_;
~CB()
{
cout<<"delete B"<<endl;
}
};
int main()
{
{
shared_ptr<CA> spa(new CA());
shared_ptr<CB> spb(new CB());
spa->pb_ = spb;
spb->pa_ = spa;
cout<<spb.use_count()<<endl;
cout<<spa.use_count()<<endl;
}
return 0;
}
得到输出结果:
2
2
因为互相引用,两个资源的引用计数都为2,当超出作用范围时,智能指针spa,spb析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致跳出函数时资源没有被释放(A B的析构函数没有被调用)。
如果把其中一个改为weak_ptr,如把类B里面的shared_ptr<CA> pa_改为
weak_ptr<CA> pa_;
运行结果如下
2
1
delete A
delete B
A的引用开始就只有1,当spa析构时,A的计数变为0,A得到释放,A释放的同时也会使B的计数减一,同时spb析构时使B的计数减一,那么B的计数为0,B得到释放。如果是把A里面的改为weak_ptr,就会发现是B先析构。