请选择 进入手机版 | 继续访问电脑版

[C 语言] 一文把握 C++ 智能指针的使用 方法

[复制链接]
查看234 | 回复49 | 2021-9-14 09:13:25 | 显示全部楼层 |阅读模式
目次

一文把握
 C++ 智能指针的使用

方法

一、RAII 与引用计数

相识

  1. Objective-C/Swift
复制代码
的程序员应该知道引用计数的概念。引用计数这种计数是为了防止内存走漏 而产生的。

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

在传统C++中,『记得』手动开释 资源,总不是最佳实践。由于 我们很有大概 就忘记了去开释 资源而导致走漏 。以是 通常的做法是对于一个对象而言,我们在构造函数的时间 申请空间,而在析构函数(在离开 作用域时调用)的时间 开释 空间, 也就是我们常说的

  1. RAII
复制代码
资源获取即初始化技术。

凡事都有破例 ,我们总会有必要 将对象在自由存储上分配的需求,在传统 C++ 里我们只好利用

  1. new
复制代码
  1. delete
复制代码
去 『记得』对资源举行 开释 。而 C++11 引入了智能指针的概念,利用 了引用计数的想法,让程序员不再必要 关心手动开释 内存。

这些智能指针就包括

  1. std::shared_ptr std::unique_ptr std::weak_ptr
复制代码
,利用 它们必要 包含头文件
  1. <memory>。
复制代码

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

二、std::shared_ptr

  1. std::shared_ptr
复制代码
是一种智能指针,它可以或许 记录多少个
  1. shared_ptr
复制代码
共同指向一个对象,从而消除显式的调用
  1. delete
复制代码
,当引用计数变为零的时间 就会将对象主动 删除。

但还不够,由于 利用

  1. std::shared_ptr
复制代码
仍然 必要 利用
  1. new
复制代码
来调用,这使得代码出现了某种程度上的不对称。

  1. std::make_shared
复制代码
就可以或许 用来消除显式的利用
  1. new
复制代码
,以是
  1. std::make_shared
复制代码
会分配创建传入参数中的对象, 并返回这个对象范例 的
  1. std::shared_ptr
复制代码
指针。比方 :

  1. #include <iostream>
  2. #include <memory>
  3. void foo(std::shared_ptr<int> i)
  4. {
  5. (*i)++;
  6. }
  7. int main()
  8. {
  9. // auto pointer = new int(10); // illegal, no direct assignment
  10. // Constructed a std::shared_ptr
  11. auto pointer = std::make_shared<int>(10);
  12. foo(pointer);
  13. std::cout << *pointer << std::endl; // 11
  14. // The shared_ptr will be destructed before leaving the scope
  15. return 0;
  16. }
复制代码

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

  1. auto pointer = std::make_shared<int>(10);
  2. auto pointerpointer2 = pointer; // 引用计数+1
  3. auto pointerpointer3 = pointer; // 引用计数+1
  4. int *p = pointer.get(); // 这样不会增加引用计数
  5. std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
  6. std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
  7. std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3
  8. pointer2.reset();
  9. std::cout << "reset pointer2:" << std::endl;
  10. std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
  11. std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0, pointer2 已 reset
  12. std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
  13. pointer3.reset();
  14. std::cout << "reset pointer3:" << std::endl;
  15. std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
  16. std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
  17. std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 0, pointer3 已 reset
复制代码

三、std::unique_ptr

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

  1. std::unique_ptr<int> pointer = std::make_unique<int>(10); // make_unique 从 C++14 引入
  2. std::unique_ptr<int> pointerpointer2 = pointer; // 非法
复制代码

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

  1. template<typename T, typename ...Args>
  2. std::unique_ptr<T> make_unique( Args&& ...args ) {
  3. return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
  4. }
复制代码

至于为什么没有提供,C++ 标准委员会主席

  1. Herb Sutter
复制代码
在他的博客中提到缘故起因 是由于 『被他们忘记了』。

既然是独占,换句话说就是不可复制。但是,我们可以利用

  1. std::move
复制代码
将其转移给其他的
  1. unique_ptr
复制代码
,比方 :

  1. #include <iostream>
  2. #include <memory>
  3. struct Foo {
  4. Foo() { std::cout << "Foo::Foo" << std::endl; }
  5. ~Foo() { std::cout << "Foo::~Foo" << std::endl; }
  6. void foo() { std::cout << "Foo::foo" << std::endl; }
  7. };
  8. void f(const Foo &) {
  9. std::cout << "f(const Foo&)" << std::endl;
  10. }
  11. int main() {
  12. std::unique_ptr<Foo> p1(std::make_unique<Foo>());
  13. // p1 不空, 输出
  14. if (p1) p1->foo();
  15. {
  16. std::unique_ptr<Foo> p2(std::move(p1));
  17. // p2 不空, 输出
  18. f(*p2);
  19. // p2 不空, 输出
  20. if(p2) p2->foo();
  21. // p1 为空, 无输出
  22. if(p1) p1->foo();
  23. p1 = std::move(p2);
  24. // p2 为空, 无输出
  25. if(p2) p2->foo();
  26. std::cout << "p2 被销毁" << std::endl;
  27. }
  28. // p1 不空, 输出
  29. if (p1) p1->foo();
  30. // Foo 的实例会在离开作用域时被销毁
  31. }
复制代码

四、std::weak_ptr

假如 你细致 思索

  1. std::shared_ptr
复制代码
就会发现依然存在着资源无法开释 的题目 。看下面这个例子:

  1. struct A;
  2. struct B;
  3. struct A {
  4. std::shared_ptr<B> pointer;
  5. ~A() {
  6. std::cout << "A 被销毁" << std::endl;
  7. }
  8. };
  9. struct B {
  10. std::shared_ptr<A> pointer;
  11. ~B() {
  12. std::cout << "B 被销毁" << std::endl;
  13. }
  14. };
  15. int main() {
  16. auto a = std::make_shared<A>();
  17. auto b = std::make_shared<B>();
  18. a->pointer = b;
  19. b->pointer = a;
  20. }
复制代码

运行结果 是

  1. A
复制代码
,
  1. B
复制代码
都不会被烧毁 ,这是由于
  1. a,b
复制代码
内部的
  1. pointer
复制代码
同时又引用了
  1. a,b
复制代码
,这使得 a,b 的引用计数均变为了
  1. 2
复制代码
,而离开 作用域时,a,b 智能指针被析构,却只能造成这块地区 的引用计数减一。

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

一文把握
 C++ 智能指针的使用

方法

办理 这个题目 的办法就是利用 弱引用指针

  1. std::weak_ptr,std::weak_ptr
复制代码
是一种弱引用(相比较而言
  1. std::shared_ptr
复制代码
就是一种强引用)。

弱引用不会引起引用计数增长 ,当换用弱引用时间 ,终极 的开释 流程如图 2 所示:

一文把握
 C++ 智能指针的使用

方法

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

  1. std::weak_ptr
复制代码
没有 * 运算符和 -> 运算符,以是 不可以或许 对资源举行 操作,它的唯一作用就是用于检查
  1. std::shared_ptr
复制代码
是否存在,其
  1. expired()
复制代码
方法能在资源未被开释 时,会返回
  1. false
复制代码
,否则返回
  1. true
复制代码

五、总结

智能指针这种技术并不新奇 ,在很多语言中都是一种常见的技术,当代 C++ 将这项技术引进,在肯定 程度上消除了

  1. new/delete
复制代码
的滥用,是一种更加成熟的编程范式。

到此这篇关于一文把握 C++ 智能指针的利用 方法的文章就先容 到这了,更多干系 C++ 智能指针的利用 内容请搜索 脚本之家从前 的文章或继续欣赏 下面的干系 文章盼望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 老鼠飞出亚洲言 | 2021-9-14 11:12:20 | 显示全部楼层
admin楼主的等级很高啊!
回复

使用道具 举报

avatar 摸金狂人 | 2021-9-14 18:22:09 | 显示全部楼层
有钱、有房、有车,人人都想!
回复

使用道具 举报

avatar V刘晨曦 | 2021-9-18 21:39:20 | 显示全部楼层
论坛的帖子越来越有深度了!
回复

使用道具 举报

avatar 萍381 | 2021-9-22 08:36:45 | 显示全部楼层
楼上的真不讲道理!
回复

使用道具 举报

avatar 秋天一且 | 2021-9-25 16:12:04 | 显示全部楼层
今天怎么了,什么人都出来了!
回复

使用道具 举报

avatar 小雨1004 | 2021-10-4 10:07:24 | 显示全部楼层
admin楼主最近很消极啊!
回复

使用道具 举报

avatar 放弃六月们 | 2021-10-12 11:18:17 | 显示全部楼层
写得实在太好了,我唯一能做的就是默默顶贴!
回复

使用道具 举报

avatar 升密示 | 2021-10-14 12:52:54 | 显示全部楼层
楼上是GG还是MM啊?
回复

使用道具 举报

avatar 路人52014 | 2021-10-15 20:28:01 | 显示全部楼层
谢谢admin楼主的分享!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则