C++2.0新特性(七)——<Smart Pointer(智能指针)之weak_ptr>

一、weak_ptr出现的意义

  上一节提到过shared_ptr,它会自动释放“不再需要使用的对象”的相应的资源,但是它不是万能的,在某些时候(比如说循环引用),它会显得力不从心,这就是weak_ptr出现的意义;

1.1 weak_ptr 使用特性

  weak_ptr也是一个模板,只提供能接受一个shared_ptr的构造函数或者另一个weak_ptr的赋值,也就是说不能直接用它定义一个智能指针对象,它是为了搭配shared_ptr使用的,weak_ptr提供lock、swap、reset、expired、operator=、use_count等函数,相对shared_ptr多了lock、expired函数,却少了get函数,也不支持operator* 和 operator->

二、weak_ptr使用测试

2.1 以下例子在shared_ptr循环引用时的弊端

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 #include <memory>
 5 using namespace std;
 6
 7 class Person {
 8 public:
 9     string name;
10     shared_ptr<Person> mother;
11     shared_ptr<Person> father;
12     vector<shared_ptr<Person>> kids;
13
14     Person(const string& n,
15         shared_ptr<Person> m = nullptr,
16         shared_ptr<Person> f = nullptr)
17         : name(n), mother(m), father(f) {
18     }
19
20     ~Person() {
21         cout << "delete " << name << endl;
22     }
23 };
24
25 shared_ptr<Person> initFamily(const string& name)
26 {
27     shared_ptr<Person> mom(new Person(name + "‘s mom"));
28     shared_ptr<Person> dad(new Person(name + "‘s dad"));
29     shared_ptr<Person> kid(new Person(name, mom, dad));
30     //以下是为了统计引用次数
31     cout << "1 mom is shared " << mom.use_count() << " times" << endl;
32     cout << "1 dad is shared " << dad.use_count() << " times" << endl;
33     cout << "1 kid is shared " << kid.use_count() << " times" << endl;
34     mom->kids.push_back(kid);
35     dad->kids.push_back(kid);
36     cout << "mom is shared " << mom.use_count() << " times" << endl;
37     cout << "dad is shared " << dad.use_count() << " times" << endl;
38     cout << "kid is shared " << kid.use_count() << " times" << endl;
39     return kid;
40 }
41
42 int main()
43 {
44     shared_ptr<Person> p = initFamily("nico");
45
46     cout << "nico‘s family exists" << endl;
47     cout << "- nico is shared " << p.use_count() << " times" << endl;
48     cout << "- name of 1st kid of nico‘s mom: "
49         << p->mother->kids[0]->name << endl;
50
51     p = initFamily("jim");
52     cout << "jim‘s family exists" << endl;
53 }

  上述例子解释:首先我们initFamily函数建立Person:mom 、dad和kid,根据传入的实参将所有姓名初始化,并且还将kid插入到其父母的容器中,最终initFamily函数返回kid并赋值给p,p其实指向上述家庭的最后一个handle,因此在p被赋值之前nico共被共享3次,现在如果我们释放这个p(释放方式:(1)给p指向一个新的person或者赋值为nullptr;(2)main()函数结束时离开p的作用域),但是我们看到程序输出的结果是没有任何person被释放(没有执行析构函数),因为他们都至少被一个shared_ptr指向,于是析构函数都无法执行,这就是循环指向的问题。

  如下是引入weak_ptr来解决循环指向的问题:我们可以把kid申明为一个类型为weak_ptr类型的vector。

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 #include <memory>
 5 using namespace std;
 6
 7 class Person {
 8 public:
 9     string name;
10     shared_ptr<Person> mother;
11     shared_ptr<Person> father;
12     vector<weak_ptr<Person>> kids;  // weak pointer !!!
13
14     Person(const string& n,
15         shared_ptr<Person> m = nullptr,
16         shared_ptr<Person> f = nullptr)
17         : name(n), mother(m), father(f) {
18     }
19
20     ~Person() {
21         cout << "delete " << name << endl;
22     }
23 };
24
25 shared_ptr<Person> initFamily(const string& name)
26 {
27     shared_ptr<Person> mom(new Person(name + "‘s mom"));
28     shared_ptr<Person> dad(new Person(name + "‘s dad"));
29     shared_ptr<Person> kid(new Person(name, mom, dad));
30     //以下是为了统计引用次数
31     cout << "1 mom is shared " << mom.use_count() << " times" << endl;
32     cout << "1 dad is shared " << dad.use_count() << " times" << endl;
33     cout << "1 kid is shared " << kid.use_count() << " times" << endl;
34     mom->kids.push_back(kid);
35     dad->kids.push_back(kid);
36     cout << "mom is shared " << mom.use_count() << " times" << endl;
37     cout << "dad is shared " << dad.use_count() << " times" << endl;
38     cout << "kid is shared " << kid.use_count() << " times" << endl;
39     return kid;
40 }
41
42 int main()
43 {
44     shared_ptr<Person> p = initFamily("nico");
45
46     cout << "nico‘s family exists" << endl;
47     cout << "- nico is shared " << p.use_count() << " times" << endl;
48     cout << "- name of 1st kid of nico‘s mom: "
49         << p->mother->kids[0].lock()->name << endl;//上述使用了lock函数,lock函数来获取其对应的shared_ptr对象,下面详细解释
50
51     p = initFamily("jim");
52     cout << "jim‘s family exists" << endl;
53 }

  上述解析:关键就在于使用的weak_ptr相比于shared_ptr,没有增加kid的引用计数,所以再最后离开main作用域后能准确调用析构函数,上面例子重要的函数时lock,我们可以将mon(shared_ptr)指针直接赋值给weak_ptr(反之不行),其实weak_ptr 最重要的函数只有lock和expired两个函数,因为weak_ptr本身不会增加引用计数,所以当我们要使用时就要用lock函数,lock()会产生出一个shared_ptr(也就说使用之前必须复制到 shared_ptr)进而像普通指针一样使用。

  关键函数解析:

  • operator=

void operator=(std::weak_ptr<T> desired) noexcept;为weak_ptr赋值,weak_ptr接受shared_ptr类型的变量赋值,但是反过来是行不通的,需要使用lock函数。

  • reset

void reset() noexcept;释放被管理对象的所有权。调用后 *this 不管理对象。

  • swap

void swap( weak_ptr& r ) noexcept;交换 *this 与 r 的内容。

  • use_count

long use_count() const noexcept;返回共享被管理对象所有权的 shared_ptr 实例数量,或 ?0? ,若被管理对象已被删除,即 *this 为空。

  • expired

bool expired() const noexcept;检查被引用的对象是否已删除,等价于 use_count() == 0,但是比use_count快,若被管理对象已被删除则为 true ,否则为 false 。由于不知道对象是否已经被析构,最好使用之前先使用expired函数检测一下。

  • lock

std::shared_ptr<T> lock() const noexcept;创建新的 std::shared_ptr 对象,它共享被管理对象的所有权。若无被管理对象,即 *this 为空,则返回亦为空的 shared_ptr ,等效地返回 expired() ? shared_ptr<T>() : shared_ptr<T>(*this) ,原子地执行。

2.2 常见成员函数以及分类介绍

原文地址:https://www.cnblogs.com/laiyingpeng/p/11725754.html

时间: 2024-10-07 17:10:05

C++2.0新特性(七)——<Smart Pointer(智能指针)之weak_ptr>的相关文章

[CareerCup] 13.8 Smart Pointer 智能指针

13.8 Write a smart pointer class. A smart pointer is a data type, usually implemented with templates, that simulates a pointer while also providing automatic garbage collection. It automatically counts the number of references to a SmartPointer<T*>

Smart pointer 智能指针小总结

Smart pointer line 58之后smart pointer里的计数已经是0,所以会真正释放它引用的对象,调用被引用对象的析构函数.如果继续用指针访问,会出现如下图的内存访问异常.所以说如果选择了用智能指针,就不要再试图用其他方式再去访问对象了. 1 // sharedTest.cpp : Defines the entry point for the console application. 2 // 3 4 #include "stdafx.h" 5 #include

C++ smart pointer智能指针

  在C++中,程序员可以直接操作内存,给编程增加了不少的灵活性.但是灵活性是有代价的,程序员必须负责自己负责释放自己申请的内存,否则就会出现内存泄露.智能指针就是为了解决这个问题而存在的.它和其他指针没有本质的区别,主要的目的就是为了避免悬挂指针.内存泄露的问题.在这里,我使用对象的应用计数做了一个smart pointer,当一个对象还有引用的时候,就不执行释放内存的操作,当引用计数为0时,就执行内存释放操作,并且将指针重置为NULL. 代码如下: #include <iostream>

Android 8.0新特性介绍以及注意事项

2017年8月22日,谷歌正式发布了Android 8.0的正式版,其正式名称为:Android Oreo(奥利奥) .在此之前 临时代号叫: Android O.对应Api level 为26. 2017年12月5日 , 谷歌正式发布了Android 8.1的正式版.对应的Api Level 为27 . Powerful 强大       Secure 安全              Fast 流畅            Smart&seamiess  轻巧&无缝 Android 8.0

Day07 jdk5.0新特性&Junit&反射

day07总结 今日内容 MyEclipse安装与使用 JUnit使用 泛型 1.5新特性 自动装箱拆箱 增强for 静态导入 可变参数方法 枚举 反射 MyEclipse安装与使用(yes) 安装MyEclipse 先安装了JDK ? MyEclipse介绍 ? MyEclipse是Eclipse的一个插件: MyEclipse是需要花钱的: MyEclipse官网不在欢迎中国人登录: ? MyEclipse使用 ? 1 创建项目 选择工作空间: 工作空间路径不能有空格和中文: 工作空间以班名

Atitit.&#160;C#.net&#160;clr&#160;2.0&#160;&#160;4.0新特性

Atitit. C#.net clr 2.0  4.0新特性 1. CLR内部结构1 2. CLR 版本发展史3 3. CLR 2.0 3 4. CLR 4 新特性 概览4 4.1.1.  托管与本地代码的互操作5 4.1.2.    垃圾回收6 4.1.3.    代码约定6 4.1.4.    Corrupted state exception6 4.1.5.     新的安全模型7 4.1.6.     同一个进程,多个CLR7 4.1.7.     基本类库7 5. CLR最新发展8 6

day07 MyEclipse 安装 jdk5.0 新特性

1.myeclipse的安装和使用 * eclipse:是一个免费的开发工具    * myeclipse:是一个收费的插件,破解myeclipse,        ** 安装目录的要求: 不能有中文和空格        ** 安装完成之后,选择一个工作空间 ,这个工作空间不能有中文和空格    * 破解myeclipse        ** 运行run.bat文件,但是运行之前,必须要安装jdk,通过配置环境变量 * myeclipse的使用        * 创建一个工程          

AFNetworking 2.0 新特性讲解之AFHTTPSessionManager

AFNetworking 2.0 新特性讲解之AFHTTPSessionManager (2014-02-17 11:56:24) 转载▼     AFNetworking 2.0 相比1.0 API 接口改动还是很大的. 其中一个便是 AFURLSessionManager,当然如果你不太熟悉,或者为了兼容低版本,你依然可以选择AFHTTPRequestOperationManager,AFURLSessionManager是基于 NSURLSessionConfiguration(IOS 7

Servlet 3.0 新特性详解

转自:https://www.ibm.com/developerworks/cn/java/j-lo-servlet30/ Servlet 3.0 新特性详解 张 建平2010 年 4 月 23 日发布 WeiboGoogle+用电子邮件发送本页面 6 Servlet 3.0 新特性概述 Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布.该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用于简化 Web 应用的开发和部署.其