一、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循环引用时的弊端

 #include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std; class Person {
public:
string name;
shared_ptr<Person> mother;
shared_ptr<Person> father;
vector<shared_ptr<Person>> kids; Person(const string& n,
shared_ptr<Person> m = nullptr,
shared_ptr<Person> f = nullptr)
: name(n), mother(m), father(f) {
} ~Person() {
cout << "delete " << name << endl;
}
}; shared_ptr<Person> initFamily(const string& name)
{
shared_ptr<Person> mom(new Person(name + "'s mom"));
shared_ptr<Person> dad(new Person(name + "'s dad"));
shared_ptr<Person> kid(new Person(name, mom, dad));
//以下是为了统计引用次数
cout << "1 mom is shared " << mom.use_count() << " times" << endl;
cout << "1 dad is shared " << dad.use_count() << " times" << endl;
cout << "1 kid is shared " << kid.use_count() << " times" << endl;
mom->kids.push_back(kid);
dad->kids.push_back(kid);
cout << "mom is shared " << mom.use_count() << " times" << endl;
cout << "dad is shared " << dad.use_count() << " times" << endl;
cout << "kid is shared " << kid.use_count() << " times" << endl;
return kid;
} int main()
{
shared_ptr<Person> p = initFamily("nico"); cout << "nico's family exists" << endl;
cout << "- nico is shared " << p.use_count() << " times" << endl;
cout << "- name of 1st kid of nico's mom: "
<< p->mother->kids[]->name << endl; p = initFamily("jim");
cout << "jim's family exists" << endl;
}

  上述例子解释:首先我们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。

 #include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std; class Person {
public:
string name;
shared_ptr<Person> mother;
shared_ptr<Person> father;
vector<weak_ptr<Person>> kids; // weak pointer !!! Person(const string& n,
shared_ptr<Person> m = nullptr,
shared_ptr<Person> f = nullptr)
: name(n), mother(m), father(f) {
} ~Person() {
cout << "delete " << name << endl;
}
}; shared_ptr<Person> initFamily(const string& name)
{
shared_ptr<Person> mom(new Person(name + "'s mom"));
shared_ptr<Person> dad(new Person(name + "'s dad"));
shared_ptr<Person> kid(new Person(name, mom, dad));
//以下是为了统计引用次数
cout << "1 mom is shared " << mom.use_count() << " times" << endl;
cout << "1 dad is shared " << dad.use_count() << " times" << endl;
cout << "1 kid is shared " << kid.use_count() << " times" << endl;
mom->kids.push_back(kid);
dad->kids.push_back(kid);
cout << "mom is shared " << mom.use_count() << " times" << endl;
cout << "dad is shared " << dad.use_count() << " times" << endl;
cout << "kid is shared " << kid.use_count() << " times" << endl;
return kid;
} int main()
{
shared_ptr<Person> p = initFamily("nico"); cout << "nico's family exists" << endl;
cout << "- nico is shared " << p.use_count() << " times" << endl;
cout << "- name of 1st kid of nico's mom: "
<< p->mother->kids[].lock()->name << endl;
//上述使用了lock函数,lock函数来获取其对应的shared_ptr对象,下面详细解释 p = initFamily("jim");
cout << "jim's family exists" << endl;
}

  上述解析:关键就在于使用的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 常见成员函数以及分类介绍

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

  1. C++2.0新特性(六)——<Smart Pointer(智能指针)之shared_ptr>

    Smart Pointer(智能指针)指的是一类指针,并不是单一某一个指针,它能知道自己被引用的个数以至于在最后一个引用消失时销毁它指向的对象,本文主要介绍C++2.0提供的新东西 一.Smart P ...

  2. C++2.0新特性(八)——<Smart Pointer(智能指针)之unique_ptr>

    一.概念介绍 unique_ptr它是一种在异常发生时可帮助避免资源泄露的smart pointer,实现了独占式拥有的概念,意味着它可确保一个对象和其他相应资源在同一时间只被一个pointer拥有, ...

  3. 【C++11新特性】 C++11智能指针之weak_ptr

    如题,我们今天要讲的是C++11引入的三种智能指针中的最后一个:weak_ptr.在学习weak_ptr之前最好对shared_ptr有所了解.如果你还不知道shared_ptr是何物,可以看看我的另 ...

  4. [CareerCup] 13.8 Smart Pointer 智能指针

    13.8 Write a smart pointer class. A smart pointer is a data type, usually implemented with templates ...

  5. C++ smart pointer智能指针

      在C++中,程序员可以直接操作内存,给编程增加了不少的灵活性.但是灵活性是有代价的,程序员必须负责自己负责释放自己申请的内存,否则就会出现内存泄露.智能指针就是为了解决这个问题而存在的.它和其他指 ...

  6. Smart pointer 智能指针小总结

    Smart pointer line 58之后smart pointer里的计数已经是0,所以会真正释放它引用的对象,调用被引用对象的析构函数.如果继续用指针访问,会出现如下图的内存访问异常.所以说如 ...

  7. 【C++11新特性】 C++11智能指针之shared_ptr

    C++中的智能指针首先出现在“准”标准库boost中.随着使用的人越来越多,为了让开发人员更方便.更安全的使用动态内存,C++11也引入了智能指针来管理动态对象.在新标准中,主要提供了shared_p ...

  8. Smart Pointer 智能指针

    P76 参考:http://www.cnblogs.com/lanxuezaipiao/p/4132096.html http://blog.csdn.net/hackbuteer1/article/ ...

  9. Javaweb学习笔记——(七)——————myexlipse基本使用、jdk5.0新特性及反射讲解

    1.debug调试模式: *使用这种模式,调试程序(看到程序运行停止在这一行) -显示出来行号 -双击左边,出现一个圆点,表示设置了一个断点 *使用debug as方式,运行程序 -特使是否进入到调试 ...

随机推荐

  1. mvc和mvvm模式

    一. Mvvm定义 MVVM是Model-View-ViewModel的简写.即模型-视图-视图模型.[模型]指的是后端传递的数据.[视图]指的是所看到的页面.[视图模型]mvvm模式的核心,它是连接 ...

  2. Web开发基础知识

    综述 最近开始Java Web方面的工作,千里之行始于足下,我们在开发过程中要善于总结自己遇到的问题.善于管理一些优秀的代码片段.本文的主要内容是Web开发的基础知识,对于大牛来说可以忽略,对于初入W ...

  3. js计算两个时间差 天 时 分 秒 毫秒

    // 计算两个时间差 dateBegin 开始时间 function timeFn(dateBegin) { //如果时间格式是正确的,那下面这一步转化时间格式就可以不用了 var dateEnd = ...

  4. MySQL NULL--三值逻辑(Three Value Logic)

    三值逻辑(Three Value Logic) 在关系型数据库中,由于NULL值的存在,导致逻辑表达式存在三种值:TRUE/FALSE/UNKNOW. SELECT '=NULL AS C1, ' A ...

  5. MySQL Install--MySQL安装相关知识和问题

    1.mysqld和mysqld_safe之间关系 mysqld_safe是一个启动脚本,用来启动MySQL服务器并监控MySQL服务的运行情况,并在其服务出现故障宕机后重启其服务. mysql.ser ...

  6. linux查看磁盘分区

    df  查看磁盘分区使用状况 用法:df [选项]... [文件]... Show information about the file system on which each FILE resid ...

  7. Pthon魔术方法(Magic Methods)-可调用对象

    Pthon魔术方法(Magic Methods)-可调用对象 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.可调用对象方法 __call__: 类中定义一个该方法,实例就可以像 ...

  8. Kali和Metasploitable2的网络配置

    Kali和Metasploitable2的网络配置 2017年06月19日 16:00:00 weixin_34275734 阅读数 389   原文链接:https://blog.csdn.net/ ...

  9. dijkstra,belllman-ford,spfa最短路算法

    参考博客 时间复杂度对比: Dijkstra:  O(n2) Dijkstra + 优先队列(堆优化):  O(E+V∗logV) SPFA:  O(k∗E) ,k为每个节点进入队列的次数,一般小于等 ...

  10. swagger2 注解说明

    整体说明 用于controller类上 注解 说明 @Api 协议集描述 方法上 注解 说明 @ApiOperation - @ApiImplicitParams 方法上 @ApiImplicitPa ...