为什么不vector :: clear从向量中删除元素?

Mad*_*iya 31 c++ stdvector undefined-behavior

当我clear()在a上使用std::vector它时,它应该销毁它中的所有元素vector,而不是它.

示例代码:

vector<double> temp1(4);
cout << temp1.size() << std::endl;
temp1.clear();
cout << temp1.size() << std::endl;

temp1[2] = 343.5; // I should get segmentation fault here ....

cout << "Printing..... " << temp1[2] << endl;
cout << temp1.size() << std::endl;
Run Code Online (Sandbox Code Playgroud)

现在,我应该在尝试访问已清除的向量时遇到分段错误,但是它会填充那里的值(据我说这是非常错误的)

结果如下:

4
0
Printing..... 343.5
0
Run Code Online (Sandbox Code Playgroud)

这是正常的吗?这是一个非常难以发现的错误,它基本上杀了我的代码数月.

Ker*_* SB 76

您无权获得分段错误.就此而言,分段错误甚至不是C++的一部分.您的程序正在从向量中删除所有元素,并且您非法访问容器超出范围.这是未定义的行为,这意味着任何事情都可能发生.确实发生了一些事情.


小智 29

当您在向量的边界外访问时,您将获得未定义的行为.这意味着一切都会发生.任何东西.

所以你可以得到旧值,垃圾或seg-fault.你不能依赖任何东西.

如果要进行边界检查,请使用at()成员函数而不是operator [].它将抛出异常而不是调用未定义的行为.


0x4*_*2D2 27

从cppreference:

void clear();
Run Code Online (Sandbox Code Playgroud)

从容器中删除所有元素.使引用包含元素的任何引用,指针或迭代器无效.可能使任何过去的迭代器无效.许多实现在调用之后不会释放已分配的内存clear(),从而有效地保持向量的容量不变.

所以没有明显问题的原因是因为向量仍然具有存储中可用的存储器.当然,这只是特定于实现的,但不是错误.此外,正如其他答案所指出的那样,您的程序也确实具有未定义的行为以便首先访问已清除的内容,因此技术上任何事情都可能发生.

  • @ 0x499602D2序列容器要求(§23.2.3/表100)状态*a.clear() - 销毁a中的所有元素.使引用a的元素的所有引用,指针和迭代器无效并且可能使过去的迭代器无效.*cppreference关于`capacity()`未更改,而且last-the-end迭代器保持有效是错误的. (2认同)

Pix*_*ist 8

让我们想象你是富有的(也许你是或者你不是......无论如何)!

由于你很富裕,你可以在Moorea(法国波利尼西亚向风群岛)购买一片土地.你非常肯定这是一个不错的财产,所以你在那个岛上建造一个别墅,你住在那里.你的别墅有游泳池,网球场,大车库和更好的东西.

过了一段时间,你离开了Moorea,因为你认为它变得非常无聊.很多运动但人很少.你出售你的土地和别墅,并决定搬到别的地方.

如果你回来一段时间后你可能会遇到很多不同的事情,但你甚至不能确定其中一个.

  • 您的别墅可能已经不在了,取而代之的是俱乐部酒店.
  • 你的别墅可能还在那里.
  • 岛上可能是沉没的.
  • ...

谁知道?虽然别墅可能不再属于你,但你甚至可以跳进游泳池或再次打网球.旁边还有另一个别墅,您可以在更大的游泳池游泳,没有人分散您的注意力.

如果你再次回来,你无法保证发现你会发现什么,而且你的矢量包含了我所看到的实现中的三个指针:(名称可能不同,但功能大致相同) .)

  • begin 指向分配的内存位置的开始(即X)
  • end 它指向分配的内存+1的结尾(即开始+4)
  • last 它指向容器+1中的最后一个元素(即开始+4)

通过调用清除容器可能会破坏所有元素并重置last = begin;.该函数size()最有可能return last-begin;,因此您将观察容器大小为0.尽管如此,begin可能仍然有效并且可能仍然分配了内存(end可能仍然是begin+4).您甚至可以在clear()之前观察您设置的值.

std::vector<int> a(4);
a[2] = 12;
cout << "a cap " << a.capacity() << ", ptr is " << a.data() << ", val 2 is " << a[2] << endl;
a.clear();
cout << "a cap " << a.capacity() << ", ptr is " << a.data() << ", val 2 is " << a[2] << endl;
Run Code Online (Sandbox Code Playgroud)

打印:

a cap 4,ptr是00746570,val 2是12
a cap 4,ptr是00746570,val 2是12

你为什么不观察到任何错误?这是因为std::vector<T>::operator[]不执行任何超出界限的检查(与之相反std::vector<T>::at()).由于C++不包含"段错误",因此您的程序似乎运行正常.

注意:在MSVC 2012上,operator[]如果在调试模式下编译,则执行边界检查.

欢迎来到未定义行为的土地!事情可能会也可能不会发生.你可能甚至无法对单一情况采取任何措施.您可以冒险并大胆地查看它,但这可能不是生成可靠代码的方法.

  • 喜欢这个比喻. (2认同)

Ant*_*nio 6

operator[]是有效的,但需要付出代价:它不执行边界检查.

有一种更安全而有效的方法来访问矢量,比如迭代器等.

如果你需要一个随机访问的向量(即并不总是顺序),要么对你编写程序的方式要非常小心,要么使用效率较低的数据at(),这在相同的条件下会抛出异常.