记一次pop_front引发coredump问题

记一次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++官方对于这种场景的定义如下。

pastedGraphic.png

从手册中可以看出对于容器内元素为空的这种场景,官方的行为是未定义的。也就是说这种场景,取决于不同平台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++官方对于这种处理都是未定义的。

发表评论

电子邮件地址不会被公开。 必填项已用*标注