智能指针(Smart pointers)是C++中的一种特殊类型,用于管理动态分配的内存资源。智能指针通过封装指针,并在适当的时机自动释放内存,从而避免内存泄漏和悬空指针等常见问题。
unique_ptr
❓为什么叫做unique ptr?
unique_ptr不能复制:
如果复制一个unique_ptr,那么将会有两个指针指向同一块内存,其中一个死了就会释放这块内存,此时另一个指针指向了已经被释放的内存。
▶️unique_ptr的使用
*使用智能指针需要#include <memory>
std::unique_ptr<Entity> entity(new Entity);
unique_ptr不支持下面这种隐式构造,因为它的构造函数为explicit,需要显示构造。
// 不存在从 "Entity *" 转换到 "std::unique_ptr<Entity, std::default_delete<Entity>>" 的适当构造函数
std::unique_ptr<Entity> entity = new Entity();
▶️使用std::make_unique
保证安全
如果构造函数抛出异常,可能会得到一个空悬指针导致内存泄漏。
// C++14支持make_unique
std::unique_ptr<Entity> entity = std::make_unique<Entity>();
▶️作用域指针,超出作用域时,它就会被销毁
{
std::unique_ptr<Entity> entity(new Entity);
} // 离开作用域时,entity指针自动销毁
▶️不能复制unique_ptr
// 无法引用 函数 "std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp> &) [其中 _Tp=Entity, _Dp=std::default_delete<Entity>]" (已声明 所在行数:394,所属文件:"D:\\Programs\\mingw64\\lib\\gcc\\x86_64-w64-mingw32\\8.1.0\\include\\c++\\bits\\unique_ptr.h") -- 它是已删除的函数
std::unique_ptr<Entity> entity(new Entity);
std::unique_ptr<Entity> e0 = entity;
unique_ptr的定义中,删除了拷贝构造函数和拷贝构造符:
// Disable copy from lvalue.
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
shared_ptr
▶️shared_ptr一般使用引用计数来实现
跟踪指针有多少个引用,当引用计数达到零,这块被指向的内存就被释放。
▶️使用std::make_shared
提高效率
std::shared_ptr<Entity> sharedEntity(new Entity);
shared_ptr需要分配另一块内存(control block),用来存储引用计数。如果先创建一个new Entity,再传递给shared_ptr构造函数,必须做两次分配:先做一次new Entity的分配,然后是control block的分配。使用make_shared可以避免这一问题。
std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
▶️可以复制shared_ptr
std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
std::shared_ptr<Entity> e0 = sharedEntity;
▶️所有的shared_ptr死亡后,才会释放指向的内存
{
std::shared_ptr<Entity> e0;
{
std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
e0 = sharedEntity;
} // 该作用域结束时,sharedEntity销毁,但Entity没有析构,因为e0仍持有对Entity的引用
} // 第二个作用域结束时,Entity才会析构
weak_ptr
和shared_ptr搭配使用。
weak_ptr在复制shared_ptr时,不会增加引用计数。
{
std::shared_ptr<Entity> e0;
{
std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
std::weak_ptr<Entity> weakEntity = sharedEntity;
} // 该作用域结束时,Entity就已经析构了
}
weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
借助 weak_ptr 类型指针可以获取 shared_ptr 指针的一些状态信息,比如有多少指向相同的 shared_ptr 指针、通过
expired()
判断shared_ptr指针指向的堆内存是否已经被释放等等,还可以解决shared_ptr 循环引用的问题。
什么情况下使用智能指针?
- 当需要声明一个堆分配的对象,但不希望自己清理/不想显式地管理内存。
- 尽量使用unique_ptr,因为它的开销较小。