我研究过STL矢量的实现。 vector container实现为动态数组。方法clear()用于破坏向量中的所有元素,它将向量的大小设置为0,但是容量保持不变。因此,如果我理解正确,所有元素都被称为析构函数,但动态分配的内存仍然可用。为了仍然释放它,我们可以做到:
Vec.swap( vector<T>() ); // Capacity = 0.
但是我们假设我们没有使用交换,只是明确表示。 内部实现(如果我错了,请纠正我)大约等于以下内容(以非常简单的方式):
// A contained type:
struct C {
int m;
C() : m(123){}
};
C * arr = new C[10]; // Suppose this is the internal array in the container
编辑: 我知道上面的新运算符并没有在实际实现中使用,而且STL使用了allocator,我只是用new作为测试用例来测试析构函数(这只是一个类比)。
// Calling clear() :
for(size_t i=0;i<SZ;i++)
arr[i].~C(); // Destroying ALL elements
// some other actions . . .
但是现在容量仍然是10,内存仍然有一些我们可以访问的数据:
// Accessing the vector at 0:
cout<<arr[0].m<<endl; // This prints 123
这是一种未定义的行为吗?嗯,似乎是这样,但我想肯定地知道。
也许如果我更深入地了解调用析构函数时会发生什么(关于堆栈内存),我可以肯定地知道,当程序超出函数范围时,这等于析构函数的调用,或者在退出作用域之前调用析构函数被认为与任何方法一样,并且对象的堆栈内存未被释放?
免责声明:上面的代码非常简化,以象征clear()的部分内容,以及我从研究中得出的结论,如果我错了,你可以纠正我。
答案 0 :(得分:4)
std::vector<...>::clear()
只是破坏对象并正确设置其内部记录以表明没有对象是正确的。访问被破坏的对象时,您有未定义的行为:虽然数据中的位可能没有更改,但关联的对象也可能已被销毁,并且其内存被回收用于其他目的。
在你的例子中C
只存储一个int
并且在析构函数中没有对它做任何事情,这些位可能没有改变,但是没有保证这种方式。特别是调试实现可能会浪费几个周期来故意将垃圾写入被破坏对象的内存中。
只是旁注:std::vector<...>
不会使用new C[n]
,而是通过分配器分配和释放原始内存。但是,这是一个细节。
答案 1 :(得分:0)
如果我理解正确,你的主要问题是:
这是一种未定义的行为吗?
使用常规数组,是的,您有一个未定义的行为,原因在上一个答案中解释。但是,如果您尝试访问std::vector
的元素,其索引等于或大于 size (注意,不要超出容量)向量,然后访问操作符将抛出std::out_of_range
异常(尽管函数clear()
确实调用向量的每个元素的析构函数并且不更改其容量,它会更改向量包含多少元素的内部计数。
答案 2 :(得分:0)
您的程序用于内存区域:堆栈和堆栈。您将arr向量地址放在堆内存中,但m的值驻留在堆栈上。 如果要删除堆内存,请使用delete [] arr,如果要使用未解决的内存,请使用
ARR = NULL 你的结构可以很容易地修改
struct C {
int *m;
C(int value) {
m=new int(value);
}
~C()
{
delete m;
}
};