4

我研究了 STL 向量的实现。矢量容器被实现为动态数组。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

编辑:我知道上面的 new 运算符没有在实际实现中使用,并且 STL 使用分配器,我只是使用 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() 的部分功能,以及我从研究中得出的结论,如果我错了,您可以纠正我。

4

3 回答 3

4

std::vector<...>::clear()只是销毁对象并适当设置其内部记录以指示没有对象是正确的。当访问被破坏的对象时,你有未定义的行为:虽然数据中的位可能没有改变,但关联的对象也可能已经被破坏,并且它们的内存可能被回收用于其他目的。

在您的示例中,C仅在析构函数中存储一个int并且不对其执行任何操作,这些位可能没有变化,但不能保证这种方式。特别是调试实现可能会浪费几个周期来故意将垃圾写入被破坏对象的内存中。

只是一个旁注:std::vector<...>不会使用new C[n]而是通过分配器分配和释放原始内存。不过,这是一个细节。

于 2012-12-29T23:30:08.800 回答
0

您的程序使用内存区域:堆栈和堆。您将 arr 向量地址放在堆内存上,但 m 的值驻留在堆栈上。如果要删除堆内存使用 delete [] arr,如果要删除未寻址的内存使用

arr=NULL 您的结构可以像这样轻松修改

struct C {
int *m;
C(int value) {
    m=new int(value);
}
~C()
{
    delete m;
}

};

于 2012-12-29T23:58:51.587 回答
0

如果我理解正确,您的主要问题是:

这是未定义的行为吗?

使用常规数组,是的,由于上一个答案中解释的原因,您有一个未定义的行为。但是,如果您尝试访问std::vector索引等于或大于向量大小(注意,不要超出容量std::out_of_range)的元素,则访问运算符将引发异常(尽管函数clear()确实调用了每个向量的元素并且改变其容量,它确实改变了向量包含多少元素的内部计数)。

于 2012-12-29T23:35:56.753 回答