记一次pop_front引发coredump问题
一 deque日常用法
C++ STL里面的deque是一个双端队列,在项目开发中我们经常将一些有先后顺序的数据保存在队列里面,以保证应用程序处理数据的顺序是合理的,例如12306在处理用户购买火车票的订单,一般就会用一个队列缓存用户的请求。
二 pop_front函数异常场景
在实际项目中,对于队列的常见操作就是从尾部插入数据,从头部取出数据。这个时候就需要使用2个函数,push_back()和pop_front()。可是这2个函数使用方法不对,就会带来意外惊喜。例如,下面这个例子。
#include <deque> { 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; }
程序运行结果如下:
# ./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++官方推荐写法
// 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++官方对于这种处理都是未定义的。