项目场景:
提示:这里简述项目相关背景:
例如:项目场景:示例:通过蓝牙芯片(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消失了,程序可以正常结束,完美!
总结
智能指针虽然将我们从管理指针的操作中释放出来,但是也要小心使用,不然就会出现一些意想不到的错误。