C++ 智能指针

C++11 智能指针

C++11 引入了 3 个智能指针类型:

  1. std::unique_ptr<T> :独占资源所有权的指针。
  2. std::shared_ptr<T> :共享资源所有权的指针。
  3. 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
  1. 使用裸指针时,要记得释放内存。
{
    int* p = new int(100);
    // ...
    delete p;  // 要记得释放内存
}
  1. 使用 unique_ptr 自动管理内存。
{
    std::unique_ptr<int> uptr = std::make_unique<int>(200);
    //...
    // 离开 uptr 的作用域的时候自动释放内存
}
  1. 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;
   }
}
  1. 自定义 deleter。
{
    struct FileCloser {
        void operator()(FILE* fp) const {
            if (fp != nullptr) {
                fclose(fp);
            }
        }   
    };  
    std::unique_ptr<FILE, FileCloser> uptr(fopen("test_file.txt", "w"));
}
  1. 使用 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. 指向共享资源的指针。
  2. 引用计数等共享资源的控制信息——实现上是维护一个指向控制信息的指针。

创建方式:

//初始化方式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 对象。
posted @ 2023-08-22 20:12  洋綮  阅读(33)  评论(0)    收藏  举报