类中使用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. Go语言中Kill子进程的正确姿势

    场景 我们在编写部署系统的时候,通常需要在机器上部署一个agent,用来执行部署脚本,为了防止部署脚本写的有问题,长时间hang住,我们通常会为脚本的执行设置一个超时时间,到了时间之后就kill掉该脚 ...

  2. 慕课DJANGO配置

    重写内置的错误处理视图 在项目urls.py中添加配置 handler500 = "app01.views.page_500" handler404 = "app01.v ...

  3. url编码方式

    url编码在线工具: http://tool.chinaz.com/tools/urlencode.aspx url编码:风景->%e9%a3%8e%e6%99%af 附HTTP请求过程: ht ...

  4. 【ClickHouse】0:clickhouse学习4之表相关操作

    Clickhouse对表操作分为四大类:增删查改(INSERT,DROP,SELECT,ALTER). 增,删,查比较简单,改最复杂.那具体有哪些改的操作呢?如下清单: ALTER ALTER TAB ...

  5. 【ClickHouse】4:clickhouse基本操作二 建库建表导数据

    背景介绍: 有三台CentOS7服务器安装了ClickHouse HostName IP 安装程序 程序端口 centf8118.sharding1.db 192.168.81.18 clickhou ...

  6. Mybatis 中 foreach 的四种用法

    foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合. foreach元素的属性主要有 item,index,collection,open,separator,close. ...

  7. JavaScript -- 变量 --手稿

  8. API是什么

    API就是接口,就是通道,负责一个程序和其他软件的沟通,本质是预先定义的函数.譬如我们去办事,窗口就类似一个API,如果对于某一件不简单的事情,这个窗口能做到让我们"最多跑一次", ...

  9. oeasy教您玩转vim - 15 - # 行内查找

    行头行尾 回忆上节课内容 上次学了直接跳到开头和结尾 最重要的就是 ^.$ ^ 到开头 $ 到结尾 I 相当于^i A 相当于$a 查找帮助 还有什么呢? 还是继续在 motion 里面 ^ .$ 之 ...

  10. 洛谷P10693

    洛谷P10693 好奇怪的题目编号 思路提取 input 11 2 13 4 5 3 7 9 9 11 11 12 output 9 以人造数据为例. 首先我们让\(i\)\(\to\)\(a_i\) ...