IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ 智能指针std::shared_ptr误用:堆上同一指针被不同共享指针对象管理 -> 正文阅读

[C++知识库]C++ 智能指针std::shared_ptr误用:堆上同一指针被不同共享指针对象管理


项目场景:

提示:这里简述项目相关背景:
例如:项目场景:示例:通过蓝牙芯片(HC-05)与手机 APP 通信,每隔 5s 传输一批传感器数据(不是很大)


问题描述:

本人在复习《大话设计模式》迭代器模式时,在VS2015用C++实现了书中的例子,习惯使然,本人将用共享指针管理所有的堆上指针对象,但是在运行结束时,main函数退出之前,程序出现exception 中断,从callstack查看,应该是指针对象delete失败导致的。
在这里插入图片描述
在这里插入图片描述
C++ 源代码如下:

#include <iostream>
#include <memory>
#include <vector>

//******************Command Pattern************************
class TicketCollector;

//抽象交通工具类
class Traffic
{
private:
	std::vector<std::string> passengers;//存储乘客姓名容器

public:
	virtual std::shared_ptr<TicketCollector> CreateTickCollector() = 0;

	virtual bool AddPassenger(const std::string& strPassengerName)
	{
		passengers.push_back(strPassengerName);
		return true;
	}

	std::vector<std::string> GetPassengers() const
	{
		return passengers;
	}
};

//抽象检票员类
class TicketCollector
{
public:
	virtual std::string First() = 0;
	virtual std::string Next() = 0;
	virtual bool IsDone() = 0;
	virtual std::string CurrentItem() = 0;
	virtual ~TicketCollector() {}
};

//具体检票员类:公交车检票员,从前向后检票
class BusTicketCollector : public TicketCollector
{
private:
	std::shared_ptr<Traffic> smartBus;
	int current;

public:
	BusTicketCollector(Traffic* pTraffic) : smartBus(pTraffic), current(0) { }

	std::string First()
	{
		return smartBus->GetPassengers()[0];
	}

	std::string Next()
	{
		++current;
		if (current < smartBus->GetPassengers().size())
		{
			return smartBus->GetPassengers()[current];
		}

		return "";
	}

	bool IsDone()
	{
		return current >= smartBus->GetPassengers().size() - 1 ? true : false;
	}

	std::string CurrentItem()
	{
		return smartBus->GetPassengers()[current];
	}
};

//具体检票员类:飞机检票员,从后向前检票
class AirplaneTicketCollector : public TicketCollector
{
private:
	std::shared_ptr<Traffic> smartAirplane;
	int current;//

public:
	AirplaneTicketCollector(Traffic* pTraffic) : smartAirplane(pTraffic)
	{
		current = pTraffic->GetPassengers().size() - 1;
	}

	std::string First()
	{
		return smartAirplane->GetPassengers()[current];
	}

	std::string Next()
	{
		--current;
		if (current >= 0)
		{
			return smartAirplane->GetPassengers()[current];
		}

		return "";
	}

	bool IsDone()
	{
		return current <= 0 ? true : false;
	}

	std::string CurrentItem()
	{
		if (current < 0)
			return "";
		return smartAirplane->GetPassengers()[current];
	}
};


//具体交通工具类: 公交车
class Bus : public Traffic
{
public:
	std::shared_ptr<TicketCollector> CreateTickCollector()
	{
		return std::make_shared<BusTicketCollector>(this);
	}
};

//具体交通工具类: 飞机
class Airplane : public Traffic
{
public:
	std::shared_ptr<TicketCollector> CreateTickCollector()
	{
		return std::make_shared<AirplaneTicketCollector>(this);
	}
};


//************************Test*****************************
int main()
{
	std::shared_ptr<Traffic> traffic = std::make_shared<Bus>();
	traffic->AddPassenger("大鸟");
	traffic->AddPassenger("小菜");
	traffic->AddPassenger("小美");
	traffic->AddPassenger("小刚");
	
	std::shared_ptr<TicketCollector> ticketCollector = traffic->CreateTickCollector();

	std::cout << "公交车开始检票:" << std::endl;
	std::cout << ticketCollector->First().c_str() << " 已买票!" << std::endl;
	while (!ticketCollector->IsDone())
	{
		std::cout << ticketCollector->Next().c_str() << " 已买票!" << std::endl;
	}
	std::cout << "公交车检票完毕!\n" << std::endl;

	
	traffic = std::make_shared<Airplane>();
	traffic->AddPassenger("大鸟");
	traffic->AddPassenger("小菜");
	traffic->AddPassenger("小美");
	traffic->AddPassenger("小刚");

	ticketCollector = traffic->CreateTickCollector();
	std::cout << "飞机开始检票:" << std::endl;
	std::cout << ticketCollector->First().c_str() << " 已买票!" << std::endl;
	while (!ticketCollector->IsDone())
	{
		std::cout << ticketCollector->Next().c_str() << " 已买票!" << std::endl;
	}
	std::cout << "飞机检票完毕!\n" << std::endl;

	system("pause");
	return 0;
}

原因分析:

看到上图的第一反应,就是内存被破坏了,但是我全程用的都是只能指针啊,不应该啊。所以第一推断就是智能指针用法有问题。然后就是痛苦的分析代码阶段,把所有用到智能指针的地方全部检查了一遍,理清智能指针的前后使用场景,终于发现问题所在。

首先在Main函数中使用共享指针管理Bus对象,

std::shared_ptr<Traffic> traffic = std::make_shared<Bus>();

该对象会调用一个CreateTickCollector()函数用来创建一个共享指针管理的售票员对象:

std::shared_ptr<TicketCollector> ticketCollector = traffic->CreateTickCollector();

上述函数会创建一个公交车售票员对象:

class Bus : public Traffic
{
public:
	std::shared_ptr<TicketCollector> CreateTickCollector()
	{
		return std::make_shared<BusTicketCollector>(this);
	}
};

该公交车售票员对象构造时需要传入一个Traffic对象:

BusTicketCollector(Traffic* pTraffic) : smartBus(pTraffic), current(0) { }

重点来了,该Traffic对象会被公交车售票员对象中的共享指针对象管理:

std::shared_ptr<Traffic> smartBus;

在main函数开始时,该Traffic对象已经被一个共享指针管理,在这里又被另一个共享指针对象管理,这就是问题所在。

我们知道,一个堆上指针对象被多个共享指针管理,会造成内存重复释放的问题,即一个new指针被delete多次。

为了测试,我再main函数中注释掉其它code, 然后添加如下代码进行测试,发现报了相同问题,证实我的分析是正确的。

	int* a = new int;
	std::shared_ptr<int> s1(a);
	std::shared_ptr<int> s2(a);

# 解决方案: 既然问题是多个共享指针管理同一堆上指针对象引起的,那就只保留一个就好了,这里我把公交车售票员类和飞机检票员类中的交通工具共享指针替换为普通指针对象,修改如下:
//具体检票员类:公交车检票员,从前向后检票
class BusTicketCollector : public TicketCollector
{
private:
	Traffic* smartBus;//要检查的交通工具
	//std::shared_ptr<Traffic> smartBus;
	...
}

//具体检票员类:飞机检票员,从后向前检票
class AirplaneTicketCollector : public TicketCollector
{
private:
	Traffic* smartAirplane;//要检查的交通工具
	//std::shared_ptr<Traffic> smartAirplane;
	...
}

其它代码保持不变,重新运行,exception消失了,程序可以正常结束,完美!


总结

智能指针虽然将我们从管理指针的操作中释放出来,但是也要小心使用,不然就会出现一些意想不到的错误。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-03 11:43:10  更:2021-09-03 11:44:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:54:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码