类中使用shared_ptr()的问题

当我们先定义一个指针,然后再用这个指针构造两个智能指针

int main()
{
int* pt = new int();
std::shared_ptr<int> p1(pt);
std::shared_ptr<int> p2(pt);
std::cout << "p1.use_count() = " << p1.use_count() << std::endl;
std::cout << "p2.use_count() = " << p2.use_count() << std::endl;
return 0;
}

运行后就会报错,显示的是pt指针被重复释放了



原因是p1和p2都以为自己是唯一独占pt的智能指针,不知道还有智能指针指向pt

所以输出后发现两个引用计数都是1

如果需要不报错,就得这样写

shared_ptr<int> p2 = p1

通过p1来定义p2,它们就知道pt有两个智能指针了,就不会报错。

再来看一个代码

class client
{
public:
~client()
{
std::cout << "~client()\n";
}
}; int main()
{
client* cp = new client();
std::shared_ptr<client> csp1(cp);
std::shared_ptr<client> csp2(cp); std::cout << "csp1.use_count: " << csp1.use_count() << std::endl;
std::cout << "csp2.use_count: " << csp2.use_count() << std::endl; return 0;
}

这个报的一样的错,原理相同,问题是我们实际开发中,很多时候需要通过this指针来获取对象的内容

这个时候需要通过enable_shared_from_this来解决问题

enable_shared_from_this的使用

class client : public std::enable_shared_from_this<client>
{
public:
~client()
{
std::cout << "~client()\n";
} std::shared_ptr<client> get_ptr()
{
return shared_from_this();
}
}; int main()
{
client* cp = new client();
std::shared_ptr<client> csp1(cp);
std::shared_ptr<client> csp2 = cp->get_ptr(); std::cout << "csp1.use_count: " << csp1.use_count() << std::endl;
std::cout << "csp2.use_count: " << csp2.use_count() << std::endl; return 0;
}

将代码改写成这样,先公有继承这个模板类。

这里需要注意,在你通过shared_from_this()返回一个类的shared_ptr时,该对象必须已经被一个shared_ptr所管理,所以你不能直接csp2 = cp->get_ptr(),要在此之前先有csp1(cp)

这样的话,借助shared_from_this(),可以使得该对象只要引用计数不为零,就任意获取它的一个shared_ptr。只要还有shared_ptr持有它,它就不会消亡。

实际开发中应用,以一个服务器demo举例

首先看下面一段代码

struct client : std::enable_shared_from_this<client>
{
public:
void start()
{ }
//...其他函数
} void start()
{
std::shared_ptr<client> s = std::make_shared<client>();
s->start();
} int main()
{
start();
return 0;
}

这里用make_shared初始化了一个client的shared_ptr,make_shared会让对象和控制块可以安全地存储在连续的内存块中。它简化了内存管理,并提高了性能。但是不支持自己写删除器。

start是一个初始的函数,里面会稍后添加业务,下面我们写一个定时器。

public:
void start()
{
start_up();
} private:
void start_up()
{
_timer.expires_from_now(std::chrono::seconds(10));
_timer.async_wait(std::bind(&client::time_out, shared_from_this()));
} void time_out()
{
start_up();
}
private:
boost::asio::steady_timer _timer;

在类里面这样设计定时器,当start()调用的时候,会调用start_up()函数设置一个定时器,并且注册time_out()这个回调函数。

此时start()函数调用结束了,临时变量s的智能指针也已经释放,但是,定时器内通过调用shared_from_this(),返回了一个s管理的对象的shared_ptr给async_wait里的回调time_out()中,s管理的对象并未消亡,直到运行完回调time_out(),它才会消亡,但是回调里面如果继续调用start_up()重新设定计时器,便又会返回一个该对象的shared_ptr()传入新注册的回调time_out()内,以此类推,只要计时器不关闭,永远不会消亡。

基于这一点,可以和读写搭配起来,灵活控制当前类在什么条件下保活,什么条件下析构。

智能指针相关:enable_shared_from_this()在开发中的常见应用的更多相关文章

  1. C++智能指针的enable_shared_from_this和shared_from_this机制

    前言 之前学习muduo网络库的时候,看到作者陈硕用到了enable_shared_from_this和shared_from_this,一直对此概念是一个模糊的认识,隐约记着这个机制是在计数器智能指 ...

  2. 团队项目开发中,常见的版本控制有svn,git

    团队项目开发中,常见的版本控制有svn,git

  3. Android开发中一些常见的问题解决方案

    分享一下自己开发中遇到的一些常见问题及解决方案,方面以后快速开发少走弯路,也可以供大家一起学习. 1.开发中很常见的一个问题,项目中的listview不仅仅是简单的文字,常常需要自己定义listvie ...

  4. 5个Android开发中比较常见的内存泄漏问题及解决办法

    android中一个对象已经不需要了,但是其他对象还持有他的引用,导致他不能回收,导致这个对象暂存在内存中,这样内存泄漏就出现了.   内存泄漏出现多了,会是应用占用过多的没存,当占用的内存超过了系统 ...

  5. 游戏开发中IIS常见支持MIME类型文件解析

    游戏开发中IIS常见支持MIME类型文件解析 .apkapplication/vnd.android .ipaapplication/vnd.iphone .csbapplication/octet- ...

  6. 监听器(Listener)学习(二)在开发中的常见应用

    监听器在JavaWeb开发中用得比较多,下面说一下监听器(Listener)在开发中的常见应用: 统计当前在线人数 自定义Session扫描器 一.统计当前在线人数 在JavaWeb应用开发中,有时候 ...

  7. WEB开发中一些常见的攻击方式及简单的防御方法

    WEB开发中一些常见的攻击方式及简单的防御方法 转载:http://blog.csdn.net/seven__________7/article/details/70896913

  8. javaweb开发中的常见错误

    Javaweb中的最常见错误及其解决方法 1.200:表示成功处理业务. 2.400 请求出错: 由于语法格式有误,服务器无法理解此请求.不作修改,客户程序就 无法重复此请求. 解决办法:,遇到400 ...

  9. iOS开发中一些常见的并行处理

    本文主要探讨一些常用多任务的最佳实践.包括Core Data的多线程访问,UI的并行绘制,异步网络请求以及一些在运行态内存吃紧的情况下处理大文件的方案等.??其实编写异步处理的程序有很多坑!所以,本文 ...

  10. iOS开发中一些常见的并行处理(转)

    本文主要探讨一些常用多任务的最佳实践.包括Core Data的多线程访问,UI的并行绘制,异步网络请求以及一些在运行态内存吃紧的情况下处理大文件的方案等.

其实编写异步处理的程序有很多坑!所以,本文 ...

随机推荐

  1. C#多态性学习,虚方法、抽象方法、接口等用法举例

    1. 多态性定义   C#中的多态性是OOP(面向对象编程)的一个基本概念,它允许一个对象在不同情况下表现出不同的行为,以增强代码的可重用性和灵活性.   根据网上的教程,我们得知C#多态性分为两类, ...

  2. P8571 题解

    既然字符串的总长一定,不妨对于每个询问中的 \(s_k\) 的长度根号分治,假定分治阈值为 \(B\).下面令 \(L\) 为所有串长度总和. 对于长度大于 \(B\) 的字符串,这样的不同字符串至多 ...

  3. CF1860C 题解

    显然是一个博弈论题,考虑 dp. 定义状态 \(dp_i\) 表示先手走到 \(i\) 之后是否有必胜策略,不难发现以下几点: 若走到 \(i\) 之后无路可走,那么就必败. 若走到 \(i\) 之后 ...

  4. Ubuntu 查看用户历史记录

    Ubuntu 查看用户历史记录 1. 查看用户命令行历史记录 1. 查看当前登录账号所属用户的历史命令行记录 打开命令行,输入 history 就会看到当前登录账号所属用户的历史记录 2. 查看系统所 ...

  5. 谈谈你对 Vue 生命周期的理解?

    生命周期是什么? Vue 实例有一个完整的生命周期,也就是从 开始创建.初始化数据.编译模版.挂载 Dom -> 渲染.更新 -> 渲染.卸载等一系列过程,我们称这是 Vue 的生命周期. ...

  6. 数据库中的空值处理(reader.IsDBNull(index))

    数据库中空值的处理 -> 准备一张新表 create table nullTable ( id int primary key, name nvarchar(10) ) insert into ...

  7. 12 二次打开pdf失败

    h5 安卓 iOS均出现pdf二次打开失败

  8. element-plus如何隐藏el-row

    在 Element Plus 中,el-row 是用于布局的组件,如果你想要隐藏 el-row,你可以使用 CSS 的 display 属性将其设置为 none.以下是一个简单的示例: <tem ...

  9. [MAUI 项目实战] 笔记App(二):数据库设计

    @ 目录 Sqlite配置 创建实体 笔记实体类 笔记分组实体 笔记片段实体 笔记片段负载实体 笔记片段仓库实体 笔记模板(场景)实体 笔记片段模板实体 笔记片段模板负载实体 配置EF 创建映射 迁移 ...

  10. 03-springboot使用单元测试和日志框架

    目录 1,springboot使用单元测试 2,使用日志框架 1,springboot使用单元测试 A:springboot使用单元测试需要引入一个单元测试启动器,该启动器的坐标为: <depe ...