背景
不久前,我遇到了一些我发现非常奇怪且看似不正确的行为,我向 GCC 提交了一份错误报告。您可以在此处查看报告和回复:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47305
(我将在这里复制大部分内容。)
当时,我不明白答案,但不是 StackOverflow 的成员,也没有人问这个问题,所以我只是破解了一个解决方法并继续。但是最近,我正在重新访问这段代码,但我仍然不明白这不是错误的理由,所以......
我的问题
在我的 Mac(当前为 OS X,Darwin 12.2.0 x86_64)附带的 C++ stdlib 发行版中,第 106-116 行的实现std::vector::erase()
如下/usr/include/c++/4.2.1/vector.tcc
所示:
template<typename _Tp, typename _Alloc>
typename vector<_Tp, _Alloc>::iterator
vector<_Tp, _Alloc>::
erase(iterator __position)
{
if (__position + 1 != end())
std::copy(__position + 1, end(), __position);
--this->_M_impl._M_finish;
this->_M_impl.destroy(this->_M_impl._M_finish);
return __position;
}
请注意,destroy()
将在调用 this 之前为向量中最后一个erase()
元素调用,而不是为 指向的元素调用__position
。我认为这是不正确的——我认为它应该改为destroy()
调用__position
. 对于简单的 POD 类型,这没什么大不了的,但对于析构函数有副作用的类(例如智能指针),这可能很关键。
下面的代码说明了这个问题:
#include <vector>
#include <iostream>
class MyClass
{
int m_x;
public:
MyClass(int x) : m_x(x) { }
~MyClass()
{
std::cerr << "Destroying with m_x=" << m_x << std::endl;
}
};
int main(void)
{
std::vector<MyClass> testvect;
testvect.reserve(8);
testvect.push_back(MyClass(1));
testvect.push_back(MyClass(2));
testvect.push_back(MyClass(3));
testvect.push_back(MyClass(4));
testvect.push_back(MyClass(5));
std::cerr << "ABOUT TO DELETE #3:" << std::endl;
testvect.erase(testvect.begin() + 2);
std::cerr << "DONE WITH DELETE." << std::endl;
return 0;
}
当我在我的 Mac 上使用 g++ 版本 4.2.1(无命令行参数)编译它时,它会在我运行它时产生以下结果:
Destroying with m_x=1
Destroying with m_x=2
Destroying with m_x=3
Destroying with m_x=4
Destroying with m_x=5
ABOUT TO DELETE #3:
Destroying with m_x=5
DONE WITH DELETE.
Destroying with m_x=1
Destroying with m_x=2
Destroying with m_x=4
Destroying with m_x=5
请注意,“ABOUT TO DELETE #3”消息之后的关键行显示析构函数实际上是为我添加的第五件事(的副本)调用的。 重要的是,#3 的析构函数永远不会被调用!!
似乎那个版本erase()
需要一个范围(两个迭代器)也有类似的问题。
所以我的问题是,我期望从向量中删除的元素的析构函数被调用是错误的吗? 看来,如果你不能指望这一点,你就不能安全地在向量中使用智能指针。或者这只是 Apple 分发的 STL 矢量实现中的一个错误?我错过了一些明显的东西吗?