千家信息网

C++智能指针的使用方法

发表于:2024-09-30 作者:千家信息网编辑
千家信息网最后更新 2024年09月30日,这篇文章主要介绍"C++智能指针的使用方法",在日常操作中,相信很多人在C++智能指针的使用方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"C++智能指针的使用方法"
千家信息网最后更新 2024年09月30日C++智能指针的使用方法

这篇文章主要介绍"C++智能指针的使用方法",在日常操作中,相信很多人在C++智能指针的使用方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"C++智能指针的使用方法"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

目录
  • 一、RAII 与引用计数

  • 二、std::shared_ptr

  • 三、std::unique_ptr

  • 四、std::weak_ptr

  • 五、总结

一、RAII 与引用计数

了解 Objective-C/Swift 的程序员应该知道引用计数的概念。引用计数这种计数是为了防止内存泄露而产生的。

基本想法是对于动态分配的对象,进行引用计数,每当增加一次对同一个对象的引用,那么引用对象的引用计数就会增加一次, 每删除一次引用,引用计数就会减一,当一个对象的引用计数减为零时,就自动删除指向的堆内存。

在传统C++中,『记得』手动释放资源,总不是最佳实践。因为我们很有可能就忘记了去释放资源而导致泄露。所以通常的做法是对于一个对象而言,我们在构造函数的时候申请空间,而在析构函数(在离开作用域时调用)的时候释放空间, 也就是我们常说的 RAII 资源获取即初始化技术。

凡事都有例外,我们总会有需要将对象在自由存储上分配的需求,在传统 C++ 里我们只好使用 newdelete 去 『记得』对资源进行释放。而 C++11 引入了智能指针的概念,使用了引用计数的想法,让程序员不再需要关心手动释放内存。

这些智能指针就包括 std::shared_ptr std::unique_ptr std::weak_ptr,使用它们需要包含头文件

注意:引用计数不是垃圾回收,引用计数能够尽快收回不再被使用的对象,同时在回收的过程中也不会造成长时间的等待, 更能够清晰明确的表明资源的生命周期。

二、std::shared_ptr

std::shared_ptr 是一种智能指针,它能够记录多少个 shared_ptr 共同指向一个对象,从而消除显式的调用 delete,当引用计数变为零的时候就会将对象自动删除。

但还不够,因为使用 std::shared_ptr 仍然需要使用 new 来调用,这使得代码出现了某种程度上的不对称。

std::make_shared 就能够用来消除显式的使用 new,所以 std::make_shared 会分配创建传入参数中的对象, 并返回这个对象类型的 std::shared_ptr 指针。例如:

#include   #include   void foo(std::shared_ptr i)  {      (*i)++;  }  int main()  {      // auto pointer = new int(10); // illegal, no direct assignment      // Constructed a std::shared_ptr      auto pointer = std::make_shared(10);      foo(pointer);      std::cout << *pointer << std::endl; // 11      // The shared_ptr will be destructed before leaving the scope      return 0;  }

std::shared_ptr 可以通过 get() 方法来获取原始指针,通过 reset() 来减少一个引用计数, 并通过 use_count() 来查看一个对象的引用计数。例如:

auto pointer = std::make_shared(10);  auto pointerpointer2 = pointer; // 引用计数+1  auto pointerpointer3 = pointer; // 引用计数+1  int *p = pointer.get(); // 这样不会增加引用计数  std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3  std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3  std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3  pointer2.reset();  std::cout << "reset pointer2:" << std::endl;  std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2  std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0, pointer2 已 reset  std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2  pointer3.reset();  std::cout << "reset pointer3:" << std::endl;  std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1  std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0  std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 0, pointer3 已 reset

三、std::unique_ptr

std::unique_ptr 是一种独占的智能指针,它禁止其他智能指针与其共享同一个对象,从而保证代码的安全:

std::unique_ptr pointer = std::make_unique(10); // make_unique 从 C++14 引入  std::unique_ptr pointerpointer2 = pointer; // 非法

make_unique 并不复杂,C++11 没有提供 std::make_unique可以自行实现:

template  std::unique_ptr make_unique( Args&& ...args ) {    return std::unique_ptr( new T( std::forward(args)... ) );  }

至于为什么没有提供,C++ 标准委员会主席 Herb Sutter 在他的博客中提到原因是因为『被他们忘记了』。

既然是独占,换句话说就是不可复制。但是,我们可以利用 std::move 将其转移给其他的 unique_ptr,例如:

#include   #include   struct Foo {      Foo() { std::cout << "Foo::Foo" << std::endl; }      ~Foo() { std::cout << "Foo::~Foo" << std::endl; }      void foo() { std::cout << "Foo::foo" << std::endl; }  };  void f(const Foo &) {      std::cout << "f(const Foo&)" << std::endl;  }  int main() {      std::unique_ptr p1(std::make_unique());      // p1 不空, 输出      if (p1) p1->foo();      {          std::unique_ptr p2(std::move(p1));          // p2 不空, 输出          f(*p2);          // p2 不空, 输出          if(p2) p2->foo();          // p1 为空, 无输出          if(p1) p1->foo();          p1 = std::move(p2);          // p2 为空, 无输出          if(p2) p2->foo();          std::cout << "p2 被销毁" << std::endl;      }      // p1 不空, 输出      if (p1) p1->foo();      // Foo 的实例会在离开作用域时被销毁  }

四、std::weak_ptr

如果你仔细思考 std::shared_ptr 就会发现依然存在着资源无法释放的问题。看下面这个例子:

struct A;  struct B;  struct A {      std::shared_ptr pointer;      ~A() {          std::cout << "A 被销毁" << std::endl;      }  };  struct B {      std::shared_ptr pointer;      ~B() {          std::cout << "B 被销毁" << std::endl;      }  };  int main() {      auto a = std::make_shared();      auto b = std::make_shared();      a->pointer = b;      b->pointer = a;  }

运行结果是 A, B 都不会被销毁,这是因为 a,b 内部的 pointer 同时又引用了 a,b,这使得 a,b 的引用计数均变为了 2,而离开作用域时,a,b 智能指针被析构,却只能造成这块区域的引用计数减一。

这样就导致了 a,b 对象指向的内存区域引用计数不为零,而外部已经没有办法找到这块区域了,也就造成了内存泄露,如图 1:

解决这个问题的办法就是使用弱引用指针 std::weak_ptr,std::weak_ptr是一种弱引用(相比较而言 std::shared_ptr 就是一种强引用)。

弱引用不会引起引用计数增加,当换用弱引用时候,最终的释放流程如图 2 所示:

在上图中,最后一步只剩下 B,而 B 并没有任何智能指针引用它,因此这块内存资源也会被释放。

std::weak_ptr 没有 * 运算符和 -> 运算符,所以不能够对资源进行操作,它的唯一作用就是用于检查 std::shared_ptr 是否存在,其 expired() 方法能在资源未被释放时,会返回 false,否则返回 true

五、总结

智能指针这种技术并不新奇,在很多语言中都是一种常见的技术,现代 C++ 将这项技术引进,在一定程度上消除了 new/delete 的滥用,是一种更加成熟的编程范式。

到此,关于"C++智能指针的使用方法"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

指针 对象 智能 资源 C++ 方法 内存 输出 使用方法 作用 就是 技术 时候 学习 区域 指向 问题 分配 代码 传统 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 开源数据库建模系统 广电网络网络安全专项治理总结 陕西民主评议软件开发电话 路由器和服务器协议 全国有多少互联网科技公司 ryzen能当服务器吗 税控服务器管理系统里怎么清卡 浦东新区创新软件开发定制报价表 黑马和千峰哪个学网络安全好 网络安全大赛如何进攻 攻破实名数据库 数据库实时数据同步技术 软件开发的伦理和开发 java解析数据库url 近年来网络安全病毒数据 信息化和网络安全权利 个人电脑数据库服务器 大学生网络安全小论文题目 数据库用户审计 运维管理系统不能访问服务器 电视台网络安全责任书 全国有多少互联网科技公司 软件开发者误报反馈 大学生学校网络安全工作 我国网络安全实行什么保护 青浦区创新数据库服务商清单 以数据库为基础的信息技术 为什么软件开发要源码 数据库b s c s区别 松江区网络软件开发定制要求
0