我正在用C ++中的矢量进行一些实验。我使用的代码如下
#include <vector>
#include <iostream>
int main()
{
std::vector<float> aVector = std::vector<float>(10); // IMPORTANT LINE
std::cout << "Vector size: " << aVector.size() << std::endl;
aVector.clear();
aVector[0] = 2.2;
std::cout << "Vector size: " << aVector.size() << std::endl;
aVector.push_back(0.1);
std::cout << "Vector size: " << aVector.size() << std::endl;
aVector.clear();
std::cout << "Vector size: " << aVector.size() << std::endl;
aVector[0] = 2.1;
std::cout << "Vector size: " << aVector.size() << std::endl;
return 0;
}
如果我有向量,用N个元素初始化它,然后清除向量并尝试将赋值aVector[0] = 1.1
放到程序一直到最后没有分段错误。
另一方面,如果我创建了一个向量但没有传递初始元素的数量,那么在尝试使用aVector[0] = 1.1
分配值时,它会使程序崩溃。
出现分段错误的唯一方法是因为我试图写入无效的内存地址。因此,根据我的理解,创建向量而不是初始化意味着指向实际向量数据的指针指向无处,而初始化向量和清除时保持指向已分配内存的指针,但向量的大小仅缩小为0?是否意味着即使在清除了矢量之后,先前存储的值仍然存在?
这是规范中定义的行为吗?我希望清除擦除并释放以前分配的内存。
答案 0 :(得分:7)
向量分配的内存不限于向量使用的内存。让我们一步一步地完成该计划。请注意,我将描述实际发生的内容;正式地,当您aVector[0]
aVector.size()==0
时,无论您最初是否分配了任何元素,都会调用未定义的行为:
std::vector<float> aVector = std::vector<float>(10); // IMPORTANT LINE
这里初始化向量,默认初始化10个元素。也就是说,它为10个浮点数分配空间,并将每个浮点数初始化为零。内存现在看起来像这样:
+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
| |
| \----------------------------------------------------\
| |
V V
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
容量告诉您已分配了多少个单元格。
请注意,你可以写一下std::vector<float> aVector(10);
现在你做
std::cout << "Vector size: " << aVector.size() << std::endl;
函数std::vector<>::size()
只计算begin
和end
之间的差异。因此,对于10
和begin
之间的10个元素,您获得end
。
aVector.clear();
这会将end
重置为等于begin
并销毁超出该新端的所有对象(float
通常意味着什么都不做;但是调试版本可能会例如用NaN覆盖浮点数) 。也就是说,你的矢量现在看起来像这样:
+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
| |
|/-----/
|
V
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
你看,记忆仍在那里。
aVector[0] = 2.2;
由于std::vector<>::operator[]
不进行范围检查(尽管调试版本可能会执行范围检查),因此会导致begin
指向的float的赋值。因为即使当前没有初始化浮点数也可以分配(如果你有例如std::vector<std::string>
,情况会有所不同),这个赋值成功了,你现在有了
+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
| |
|/-----/
|
V
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 2.2 | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
接下来,
std::cout << "Vector size: " << aVector.size() << std::endl;
当然会给0
,因为这是begin
和end
之间的区别。
aVector.push_back(0.1);
成员push_back
将首先检查end
之后是否存在其他元素的空间(在这种情况下显然是这样),如果不存在则重新分配(现在不适用)然后简单地构造一个新对象在*end
和增量end
的值。所以现在你的矢量看起来像这样:
+-------+-----+-------------+
| begin | end | capacity=10 |
+-------+-----+-------------+
| |
| //
| |
V V
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 0.1 | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? | ??? |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
正如您所看到的,对于向量,2.2
实际上不存在,因此它被新构造的0.1
覆盖。
由于程序的其余部分只是重复前面的步骤(结果相同),所以我就此止步。
现在,如果在开始时你没有请求任何元素,则向量最初不会分配内存,因此尝试写入aVector[0]
将导致分段错误。
答案 1 :(得分:0)
从the doc开始,clear()
使std::vector
对象的任何元素的所有内容(引用,指针...)无效,但由于capacity()
仍然存在,因此不会释放内存不变。