C++ 智能指针
C++11 智能指针
C++11 引入了 3 个智能指针类型:
std::unique_ptr<T>
:独占资源所有权的指针。std::shared_ptr<T>
:共享资源所有权的指针。std::weak_ptr<T>
:共享资源的观察者,需要和 std::shared_ptr 一起使用,不影响资源的生命周期。
std::auto_ptr
已被废弃。
头文件:#include <memory>
1、std::unique_ptr
-
独占,在任何给定的时刻,只能有一个指针管理内存
-
当指针超出作用域时,内存将自动释放。对象销毁时会释放其持有的堆内存。
-
该类型指针不可以
Copy
,只能Move
-
对其持有的堆内存具有唯一拥有权,也就是说引用计数永远是 1。
auto_ptr
的替代品
创建方式:
- 通过已有裸指针创建
- 通过
new
来创建 - 通过
std::make_unique
创建(推荐)
//初始化方式1
std::unique_ptr<int> sp1(new int(123));
//初始化方式2
std::unique_ptr<int> sp2;
sp2.reset(new int(123));
//初始化方式3
std::unique_ptr<int> sp3 = std::make_unique<int>(123);
#移动构造 move
std::unique_ptr<int> sp3;
sp3 = std::move(sp2);
unique_ptr
可以通过 get()
获取地址
unique_ptr
实现了 ->
与 *
- 可以通过
->
调用成员函数 - 可以通过
*
调用dereferencing
- 使用裸指针时,要记得释放内存。
{
int* p = new int(100);
// ...
delete p; // 要记得释放内存
}
- 使用
unique_ptr
自动管理内存。
{
std::unique_ptr<int> uptr = std::make_unique<int>(200);
//...
// 离开 uptr 的作用域的时候自动释放内存
}
unique_ptr
可以指向一个数组。
{
std::unique_ptr<int[]> uptr = std::make_unique<int[]>(10);
for(int i = 0; i < 10; i++){
uptr[i] = i*i;
}
for(int i = 0; i < 10; i++){
std::cout << uptr[i] << std::endl;
}
}
- 自定义 deleter。
{
struct FileCloser {
void operator()(FILE* fp) const {
if (fp != nullptr) {
fclose(fp);
}
}
};
std::unique_ptr<FILE, FileCloser> uptr(fopen("test_file.txt", "w"));
}
- 使用 Lambda 的 deleter。
{
std::unique_ptr<FILE, std::function<void(FILE*)>> uptr(
fopen("test_file.txt", "w"), [](FILE* fp) {
fclose(fp);
});
}
2、std::shared_ptr
共享智能指针std::shared_ptr
能够有多个shared_ptr
共同指向一个对象,并且记录std::shared_ptr
个数从而消除显式的调用delete
,当引用计数变为0时,自动删除对象。
一个 shared_ptr 对象的内存开销要比裸指针和无自定义 deleter 的 unique_ptr 对象略大。
std::shared_ptr
需要维护的信息有两部分:
- 指向共享资源的指针。
- 引用计数等共享资源的控制信息——实现上是维护一个指向控制信息的指针。
创建方式:
//初始化方式1
std::shared_ptr<int> sp1(new int(123));
//初始化方式2
std::shared_ptr<int> sp2;
sp2.reset(new int(123));
//初始化方式3
std::shared_ptr<int> sp3;
sp3 = std::make_shared<int>(123);
为什么控制信息和每个std::shared_ptr
对象都需要保存指向共享资源的指针?可不可以去掉 std::shared_ptr
对象中指向共享资源的指针,以节省内存开销?
答案是:不能。 因为 std::shared_ptr
对象中的指针指向的对象不一定和控制块中的指针指向的对象一样。
如果有一块资源,同时又裸指针和共享指针std::share_ptr
指向它,当所有的共享指针都被摧毁,裸指针仍然存在,这个底下的资源仍然会被释放,这时再使用裸指针去访问那块资源,就会变成未定义的行为,产生严重后果,因此std::shared_ptr和裸指针尽量避免同时使用
enable_shared_from_this:继承std::enable_shared_from_this
的子类,可以使用shared_from_this
成员函数获取自身的shared_ptr指针;该类提供了允许继承类的对象创建指向自身实例的shared_ptr
,并且与存在shared_ptr
对象共享所有权。
3、std::weak_ptr
弱指针weak_ptr
weak_ptr
要与shared_ptr
一起使用。
它对资源的引用时非拥有式的,它没有对资源的管理权限,不能控制资源的释放,甚至想要访问资源时,也需要临时创建一个shared_ptr
才行。
但是weak_ptr
可以检查资源是否存在,在我们不想额外控制资源,但是又想检查资源是否存在时,就可以使用weak_ptr
。
初始化和前面两种基本相同。
为什么以及何时使用weak_ptr
?
shared_ptr
理论上可以释放内存,但仍然有一种特殊情况会造成内存泄露(环形依赖,交叉引用,导致引用计数不能降为0)。
解决:替换一个shared_ptr
即可
小结
智能指针,本质上是对资源所有权和生命周期管理的抽象:
- 当资源是被独占时,使用 std::unique_ptr 对资源进行管理。
- 当资源会被共享时,使用 std::shared_ptr 对资源进行管理。
- 使用 std::weak_ptr 作为 std::shared_ptr 管理对象的观察者。
- 通过继承 std::enable_shared_from_this 来获取 this 的 std::shared_ptr 对象。