引入智能指针的背景(目的)
智能指针的引入主要为了解决程序员在使用new之后该释放(delete)内存的地方没释放(delete),要确保用 new 动态分配的内存空间在程序的各条执行路径都能被释放是一件麻烦的事情。C++ 11 模板库的 <memory> 头文件中定义的智能指针,即 shared _ptr 模板,就是用来部分解决这个问题的。
shared_ptr
只要将 new 运算符返回的指针 p 交给一个 shared_ptr 对象“托管”,就不必担心在哪里写delete p
语句——实际上根本不需要编写这条语句,托管 p 的 shared_ptr 对象在消亡时会自动执行delete p
。而且,该 shared_ptr 对象能像指针 p —样使用,即假设托管 p 的 shared_ptr 对象叫作 ptr,那么 *ptr 就是 p 指向的对象。
通过 shared_ptr 的构造函数,可以让 shared_ptr 对象托管一个 new 运算符返回的指针,写法如下:
shared_ptr<T> ptr(new T); // T 可以是 int、char、类等各种类型
此后,ptr 就可以像 T* 类型的指针一样使用,即 *ptr 就是用 new 动态分配的那个对象。
多个 shared_ptr 对象可以共同托管一个指针 p,当所有曾经托管 p 的 shared_ptr 对象都解除了对其的托管时,就会执行delete p
。
示例代码
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
char m_c;
A(char c) : m_c(c) {}
~A() { cout<< m_c << " destrructed" << endl; }
};
int main()
{
shared_ptr<A> sp1(new A('a')); //A(a)由sp1托管,也就是A(a)的所有权归sp1
shared_ptr<A> sp2(sp1); //A('a')同时交由sp2托管
shared_ptr<A> sp3 = sp2; //A('a')同时交由sp3托管
cout<<"1-->"<<sp1->m_c<<","<<sp2->m_c<<","<<sp3->m_c<<endl;
A* pa = sp3.get(); //get()返回托管的指针, pa 指向 A('a')
cout<<"2-->"<<pa->m_c <<endl;
sp1.reset(new A('b')); //reset导致托管新的指针,此时sp1托管A('b')
sp2.reset(new A('c')); //sp2托管A('c')
sp3.reset(new A('d')); //sp3托管A('d'),A('a')无人托管,被delete
cout<<"3-->"<<sp1->m_c <<","<< sp2->m_c <<","<< sp3->m_c <<endl;
return 0;
}
运行结果
可以让多个 sharecLptr 对象托管同一个指针。这多个 shared_ptr 对象会共享一个对共同托管的指针的“托管计数”。有 n 个 shared_ptr 对象托管同一个指针 p,则 p 的托管计数就是 n。当一个指针的托管计数减为 0 时,该指针会被释放。shared_ptr 对象消亡或托管了新的指针,都会导致其原托管指针的托管计数减 1。shared_ptr 的 reset 成员函数可以使得对象解除对原托管指针的托管(如果有的话),并托管新的指针。原指针的托管计数会减 1。
注意
只有指向动态分配的对象的指针才能交给 shared_ptr 对象托管。将指向普通局部变量、全局变量的指针交给 shared_ptr 托管,编译时不会有问题,但程序运行时会出错,因为不能析构一个并没有指向动态分配的内存空间的指针。
不能用下面的方式使得两个 shared_ptr 对象托管同一个指针:
A* p = new A(66);
shared_ptr <A> sp1(p), sp2(p);
sp1 和 sp2 并不会共享同一个对 p 的托管计数,而是各自将对 p 的托管计数都记为 1(sp2 无法知道 p 已经被 sp1 托管过)。这样,当 sp1 消亡时要析构 p,sp2 消亡时要再次析构 p,这会导致程序崩溃。