在STL的stack实现中,如果想要从stack中获取一个数据并将其弹出,需要两个成员函数配合,使用top()
函数来得到这个数据,使用pop()
函数来弹出数据。问题是,为什么不能在pop()
中删除并返回元素呢?
在《C++ Concurrency In Action》书中有一段描述:
假设有一个
stack<vector<int>>
,vector是一个动态容器,当你拷贝一个vector时,标准库会从堆上分配很多内存来完成这次拷贝。当这个系统处在重度负荷,或有严重的资源限制的情况下,这种内存分配就会失败,所以vector的拷贝构造函数可能会抛出一个std::bad_alloc
异常。当vector中存有大量元素时,这种情况发生的可能性更大。当pop()
函数返回“弹出值”时(也就是从栈中将这个值移除),会有一个潜在的问题:这个值被返回到调用函数的时候,栈才被改变;但当拷贝数据的时候,调用函数抛出一个异常会怎么样?如果事情真的发生了,要弹出的数据将会丢失;它的确从栈上移出了,但是拷贝失败了!std::stack
的设计人员将这个操作分为两个部分:先获取顶部元素(top()
),然后从栈中移除元素(pop()
)。这样,在不能安全的将元素拷贝出去的情况下,栈中的这个数据还依旧存在,没有丢失。当问题是堆空间不足时,应用可能会释放一些内存,然后再进行尝试。
也就是说,如果把pop()
和top()
的功能放在一个函数中,就很可能出现上述的bug,导致原始数据丢失。
然而,由于是分离实现,这就导致这种栈的实现是线程不安全的。书中给出了线程安全的栈实现方案,关键点就是把pop()
和top()
合二为一。为了解决上述描述的问题,最终的pop()
有两种方法:一种是void pop(T& value)
(即通过引用形参来返回值),另一种是shared_ptr<T> pop()
(即通过返回数据的指针的方式)。这两种方式都不会在栈数据删除的情况下出现数据的赋值操作,所以不会发生上面所述的问题,并且又是线程安全的。