二、内存管理-智能指针
本文为《C++高级编程(第四版)》第七章内存管理部分,着重记录了如何使用智能指针。
智能指针是很好的管理内存的方式,并建议多使用智能指针而不是裸指针。
最简单的智能指针类型对资源有唯一所有权,当智能指针离开作用域或被重置时,会释放所引用内存,unique_ptr
就是这种智能指针。
有时,多个对象或代码段包含同一个指针的多个副本。这个时候就要在使用这个资源的最后一个代码块处释放指针的资源。因此,shared_ptr
就出现了,它使用“引用计数”来跟踪指针的所有者。每当复制这个智能指针的时候,都会创建一个指向同一个资源的智能指针,将引用计数加1。当一个智能指针要释放时,引用计数就会减1。当引用计数为0时,智能指针释放此资源。
头文件为<memory>
。
一、unique_ptr
一般要将动态分配的对象保存在堆栈的unique_ptr
实例中。
1.1 创建unique_ptr
一种创建智能指针的方法就是使用auto
和make_unique
。
auto test = make_unique<Test>(); // 若构造函数有参数,可以把参数放在这个括号里
test->go(); // 如果调用智能指针的方法,用.,调用智能指针指的东西的方法,用->。
如果编译器不支持此类型,还有一种。
unique_ptr<Test> test(new Test());
建议使用第一种方式创建智能指针。
1.2 使用unique_ptr
get()
方法,可获取智能指针原来指向的底层指针。
cout << test.get() << endl;
可释放指针指向的对象,也可将其改成另一个指针,使用reset()
。
test.reset(); // 释放指向的资源,此时指针指空
test.reset(new Test()); // 释放指向的资源,指向另一个指针,先创建另一个指针,再释放此资源。
可以使用release()
断开资源的连接,此方法将返回资源的底层指针,然后将智能指针置nullptr。
Test* t2 = test.release();
delete t2;
1.3 改变unique_ptr
所有权
如果要将智能指针指的资源给另一个指针,由于资源唯一,先前的指针会失效,继续使用先前指针会造成不可预料的问题,并且只能使用move
函数进行操作,不可直接用 = 操作。
auto test1 = make_unique<Test>();
unique_ptr<Test> test2 = move(test1);
cout << test1.get() << endl; // 输出为0,该指针已失效,调用会造成不可预料的情况
cout << test2.get() << endl; // 输出正确的地址
二、shared_ptr
和unique_ptr
一样,两种方式创建,建议使用make_shared()
。
使用方面略有不同,在调用reset()
时,由于引用计数,仅在最后一个指针也释放了才释放资源。
2.1 建立对象的副本
有时需要将同一个对象给多个指针,这时应该使用智能指针的复制构造函数。
auto p1 = make_shared<A>();
shared_ptr<A> p2(p1);
2.2 查看资源被多少指针占有
有时候需要查看一些该资源,有多少shared_ptr
占有,这时可以使用下面这个函数。
p1.use_count();
2.3 别名
试想,一个智能指针指向了一个类的对象,而另一个智能指针指向这个对象的一个属性。
class A
{
public:
A(int num) {m_num = num;}
int m_num;
};
int main()
{
auto p1 = make_shared<A>(1);
auto p2 = shared_ptr<int>(&p1->m_num); // 1. 利用p1的属性创建智能指针
// auto p2 = shared_ptr<int>(p1, &p1->m_num); // 2. 利用别名构造函数
return 0;
}
先看第一种创建方式,这是一种利用指针来创建指针的方式。最后p1
和p2
都会释放自己的资源,这时会发生什么?注意,shared_ptr
从来不会复制资源,资源只会有一份,shared_ptr
的工作是确定什么时候去释放。
到最后p1
和p2
都会释放资源,即资源被释放了两次,内存出错。
那如何解决这个问题呢?就是使用第二种方式,用别名构造函数。
使用别名构造函数时,p1
的资源数会加1,可以使用p1.use_count()
查看。也因为p1
的资源数加1了,所以可以正确的释放资源。