C++智能指针

引言:C++没有垃圾回收(GC)机制,需要手动开辟和释放堆内存,当释放不及时或者被遗忘时,就会导致内存泄露或者内存溢出的问题。针对该问题,C++11提供了三种智能指针(模板类):unique_ptrshared_ptrweak_ptr,使用时需引入<memory>头文件。

unique_ptr

独占型智能指针,其效果是:当unique_ptr对象离开作用域而执行析构函数时,会自动delete其关联的指针。

构造方式

  1. 构造函数,参数为裸指针。
  2. make_unique函数,C++14支持。
  3. 移动构造,移动拷贝或者移动赋值。

注意事项:unique_ptr不允许普通的拷贝和赋值(通过=delete禁用)。

std::unique_ptr<T> ptr_obj1(new T);                     // 普通构造
std::unique_ptr<T> ptr_obj2 = std::make_unique<T>(...); // make_unique构造
std::unique_ptr<T> ptr_obj3 = std::move(ptr_obj1);      // 移动拷贝
std::unique_ptr<T> ptr_obj4(std::move(ptr_obj1));       // 移动拷贝
std::unique_ptr<T> ptr_obj5;
ptr_obj5 = std::move(ptr_obj1);                         // 移动赋值

// 当关联一个对象指针时,参数...用于构造该对象
auto ptr1 = std::make_unique<T>(...);
// 当关联一个数组时,参数是数组的大小
auto ptr2 = std::make_unique<T[]>(10);

移动构造语义

假设智能指针ptr-i所关联的裸指针为p-i

auto ptr1 = std::make_unique<T>();
auto ptr2 = std::make_unique<T>();
ptr2 = std::move(ptr1);      // delete p2; p2 = p1; p1 = nullptr;
std::unique_ptr<T> ptr3;
ptr3 = std::move(ptr2);      // p3 = p2; p2 = nullptr;
auto ptr4 = std::move(ptr3); // p4 = p3; p3 = nullptr;

常用方法

get():返回关联的裸指针,不能delete,资源所有权仍然归unique_ptr
release():释放所有权,返回裸指针,之后需要手动delete
reset(p):delete关联的裸指针,获取p的所有权
reset():delete关联的裸指针,并置为nullptr
swap():交换两个unique_ptr所关联的裸指针

shared_ptr

共享型智能指针,其效果是:多个shared_ptr对象关联同一个裸指针,每多一个shared_ptr对象,引用计数加一,每少一个shared_ptr对象,引用计数减一,当引用计数降为0时,自动delete其关联的指针。

构造方式

// 普通构造与make_shared
auto ptr1 = std::make_shared<T>();
std::shared_ptr<T> ptr2(new T);

// 拷贝构造与赋值构造(对应对象引用计数+1)
auto ptr3 = ptr1;
std::shared_ptr<T> ptr4;
ptr4 = ptr2;

// 移动拷贝与移动赋值(语义和unique_ptr相同)
auto ptr5 = std::move(ptr1);
std::shared_ptr<T> ptr6;
ptr6 = std::move(ptr2);

实现原理

shared_ptr对象中包含两个指针,一个指向所管理的数据,一个指向控制块(引用计数、weak_ptr的数量、删除器、分配器等)。

  • 引用计数的修改是通过原子操作实现的,即是线程安全的。
  • 多个线程操作同一个shared_ptr对象时,例如重新赋值等,不是线程安全的。
  • 多个线程操作不同shared_ptr对象时(但管理同一数据的),是线程安全的。

常用方法

除了和unique_ptr相同的方法,shared_ptr可通过use_count()来获取所管理数据的引用计数。

weak_ptr

weak_ptr用于监视shared_ptr的生命周期,不管理shared_ptr内部的指针,不能操作资源,不改变引用计数。

基本用法

  • expired():判断监视的shared_ptr对象是否仍然存在。
  • use_count():获取监视的shared_ptr对象所管理数据的引用计数。
  • lock():获取监视的shared_ptr对象
std::weak_ptr<int> wp;
void func() {
  std::cout << "RC: " << wp.use_count() << std::endl;
  if (wp.expired()) {
    std::cout << "Expired" << std::endl;
  } else {
    auto spt = wp.lock();
    std::cout << *spt << std::endl;
  }
}

int main()
{
  {
    auto sp = std::make_shared<int>(100);
    wp = sp;
    func();
  }
  func();
  return 0;
}

// 输出:
// RC: 1
// 100
// RC: 0
// Expired

weak_ptr的两个经典应用:返回this指针;解决循环引用(待补充)

posted @ 2023-04-12 15:21  MyAJY  阅读(104)  评论(0)    收藏  举报