记一次pop_front引发coredump问题
一 deque日常用法
C++ STL里面的deque是一个双端队列,在项目开发中我们经常将一些有先后顺序的数据保存在队列里面,以保证应用程序处理数据的顺序是合理的,例如12306在处理用户购买火车票的订单,一般就会用一个队列缓存用户的请求。
二 pop_front函数异常场景
在实际项目中,对于队列的常见操作就是从尾部插入数据,从头部取出数据。这个时候就需要使用2个函数,push_back()和pop_front()。可是这2个函数使用方法不对,就会带来意外惊喜。例如,下面这个例子。
#include <deque>
int main()
{
std::deque<int32_t> deq;
deq.pop_front();
cout << “size: ” << deq.size() << endl;
return 0;
}
对于这段代码初看并没有觉得有什么问题,pop_front()函数删除队列中首元素,如果deque中没有元素,这个函数应该会直接返回。但是,实际的运行结果确实如下所示。
// MacOS 程序运行结果
zsh: segmentation fault ./test
// linux gcc 版本 4.8.5 20150623 (GCC) 运行结果
deq size: 18446744073709551615
三 场景异常原因分析
上述这段程序在MacOS上运行直接coredump,在Linux 平台下程序也出现异常。只有平台不同,说明如果deque里没有元素,调用pop_front() 这个时候在不同的平台上运行结果取决于具体实现,C++官方对于这种场景的定义如下。
从手册中可以看出对于容器内元素为空的这种场景,官方的行为是未定义的。也就是说这种场景,取决于不同平台STL库的实现。下面将linux平台下测试代码进行修改,更新后代码如下。
class UsrInfo
{
public:
UsrInfo()
{
cout << “UsrInfo” << endl;
ptr = new int;
}
~UsrInfo()
{
cout << “~UsrInfo” << endl;
delete ptr;
}
private:
int *ptr;
int rid;
int mid;
int streamId;
};
int main()
{
deque<UsrInfo> deq;
deq.pop_front();
cout << “deq size: ” << deq.size() << endl;
return 0;
}
程序运行结果如下:
[root@VM_0_15_centos code]# ./test
~UsrInfo
deq size: 18446744073709551615
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
~UsrInfo
段错误
代码里面将deque存储数据类型变为自定义类型UsrInfo,输出结果来看调用pop_front()后又调用了UsrInfo的析构函数,这个时候已经将堆栈破坏导致deque内存出现异常。程序在运行结束时出现21次UsrInfo析构函数,初步推测是由于deque在构建deq对象时就创建了21个UsrInfo对象。因此,Linux平台对于这种未定义的场景在实现上也并非如同我们所想那样。
四 场景处理总结
// C++官方推荐写法
{
std::deque<int> mydeque;
mydeque.push_back (100);
mydeque.push_back (200);
mydeque.push_back (300);
std::cout << “Popping out the elements in mydeque:”;
while (!mydeque.empty())
{
std::cout << ‘ ‘ << mydeque.front();
mydeque.pop_front();
}
std::cout << “\nThe final size of mydeque is ” << int(mydeque.size()) << ‘\n’;
return 0;
}
这种官方表示会出现未定义的函数,在日常开发中需要谨慎处理推荐参考上述C++官方写法。从C++开发手册中可以看出,deque、list、stack、vector中的pop_front()、pop_back()、pop()、front()这些函数对于容器元素为空的场景,C++官方对于这种处理都是未定义的。