当使用new关键字分配内存时,如果不回收会导致内存泄漏,因此需要在需要使用delete来释放分配的内存。然而这很容易忘记内存释放,在delete之前throw exception也会导致内存泄露。
智能指针是一直更为安全的动态内存使用方式,与常规执政不同的是它可以自动释放所指向的对象。常见的三个智能指针模板(auto_ptr、unique_ptr和shared_ptr)都定义了类似指针的对象,可以将 new 获得(直接或间接)的地址赋给这种对象。当智能指针过期时,其析构函数将使用delete来释放内存。
1、shared_ptr创建
如果程序要使用多个指向同—个对象的指针,应选择 shared_ptr。shared_ptr包含在头文件memory中,并位于命名空间std中。 shared_ptr是一个模板类,在创建时需要指定类型,也可以选择指定自己的构造方法和析构方法,也可以利用已有的智能指针进行拷贝创建。
//默认初始化,没有获取任何指针的所有权,保存着一个空指针,引用计数为0
std::shared_ptr<int> p1;
//拥有指向int的指针所有权,引用计数为1
std::shared_ptr<int> p2 (new int);
//拥有指向int的指针所有权,引用计数为1,拥有自己的析构方法
std::shared_ptr<int> p3 (new int, deleter);
//拥有指向int的指针所有权,拥有自己的构造函数与析构函数,可以为成员中的指针分配内存
std::shared_ptr<int> p4 (new int, [](int* p){delete p;}, std::allocator<int>());
//拥有指向int的指针所有权
std::shared_ptr<int> p5 (p4);
//获取指向int指针所有权,并将引用计数设置为1
std::shared_ptr<int> p6 (std::unique_ptr<int>(new int));
2、赋值
赋值有三种方式,可以通过拷贝赋值、移动,以及可以使用make_shared()一次性分配好内存。
std::shared_ptr<int> p0 = std::make_shared<int>(1)
//拷贝,引用计数二者相同
std::shared_ptr<int> p1 = p0;
cout << *p1 << endl //输出1
//移动,引用移动,后者获得前者的引用,前者引用清零
std::shared_ptr<int> p2 = p0;
cout << *p2 << endl //输出1
cout << *p0 << endl //异常
//make_shared()
std::shared_ptr<int> p3 = std::make_shared<int>(2);
cout << *p3 << endl //输出2
3、引用计数
std::shared_ptr内部维护一个引用计数来标识指向该资源的指针数目,一些知识点如下:
- std::shared_ptr的大小是原始指针的两倍,因为它的内部有一个原始指针指向资源,同时有个指针指向引用计数
- 默认初始化引用计数为0,若初始化时通过new获取指针所有权,则引用计数为1
//默认初始化,没有获取任何指针的所有权,保存着一个空指针,引用计数为0
std::shared_ptr<int> p1;
//拥有指向int的指针所有权,引用计数为1
std::shared_ptr<int> p2 (new int);
- 在拷贝赋值操作中, 若被拷贝者计数为0,则拷贝后被拷贝者和拷贝者引用计数均为0;若被拷贝者计数为n(n>0),则拷贝之后二者引用计数均为 n+1
std::shared_ptr<int> p1; //引用计数为0
std::shared_ptr<int> p2 (new int); //引用计数为1
std::shared_ptr<int> p3 = p1; //p1和p3引用计数为0
std::shared_ptr<int> p4 = p2; //p2和p4引用计数均为2
- 在移动move()操作中,被转移者引用计数清零,而转移者或者被转移者的引用计数
std::shared_ptr<int> p1 = std::make_shared<int>(1); //引用计数为1
std::shared_ptr<int> p2 = std::move(p1) //p2引用计数为1,p1引用计数为0
4、常用操作与方法
4.1 p和*p
- p :将p用作一个条件判断,若p指向一个对象,则为true
- *p:解引用p,获得它指向的对象
std::shared_ptr<int> p1 = std::make_shared<int>(1);
std::shared_ptr<int> p2();
cout << p1 << endl; //输出0x55555556df10
cout << p2 << endl; //输出0
cout << *p1 << endl; //输出1
cout << *p2 << endl; //异常
4.2 make_shared<T>(args)
安全的分配和使用动态内存的标准库函数,在动态内存中分配一个对象并使用args初始化,返回指向此类型为T的对象的shared_ptr,头文件在memory中。常见定义如下
std::shared_ptr<int> p1 = std::make_ptr<int>(2); // *p为2
std::shared_ptr<double> p2 = std::make_ptr<int>(2.2); // *p为2.2
std::shared_ptr<string> p3 = std::make_ptr<string>("hello world"); // *p为"hello world"
4.3 get()
返回p中保存的指针。要小心使用,若智能指针释放了其对象,返回指针所指向的对象也会消失。
int *p = new int(2);
std::shared_ptr<int> p1(p);
cout << *p1.get() << endl; //输出2
delete p;
cout << *p1.get() << endl; //异常
4.4 use_count()
获取引用计数
std::shared_ptr<int> p1;
cout << p1.use_count() <<endl //输出0
std::shared_ptr<int> p2 (new int);
cout << p2.use_count() <<endl //输出1
std::shared_ptr<int> p3(p2);
cout << p2.use_count() <<endl //输出2
4.5 swap(p, q)
交换p和q中的指针,也可以使用p.swap(q)的方式
std::shared_ptr<int> p1 = std::make_ptr<int>(1);
std::shared_ptr<int> p2 = std::make_ptr<int>(2);
swap(p1, p2);
cout << *p1 << endl; //输出2
cout << *p2 << endl; //输出1
p1.swap(p2);
cout << *p1 << endl; //输出1
cout << *p2 << endl; //输出2