我正在阅读关于如何实现ArrayList的数据结构书籍,并且我有以下代码用于擦除功能:
template <typename T>
void ArrayList<T>::erase(int index) {
//Delete the element whose index is "index"
//Throw illegalIndex exception if no such element
checkIndex(index);
std::copy(dynArr+ index + 1, dynArr + listSize, dynArr + index);
dynArr[--listSize].~T(); //invoke destructor
}
析构函数定义为:
template<typename T>
ArrayList<T>::~ArrayList() {
delete [] dynArr;
}
我对那到底发生了什么感到困惑。当需要删除整个数组时,析构函数不是就在那里删除吗?
答案 0 :(得分:2)
调用T的析构函数(不是ArrayList<T>
的析构函数),可能是因为T类型的对象是在预分配的数组上创建的,而不是使用特殊的placement new operator
在堆上单独创建的
因此当删除一个时,不需要释放任何内存但是你想调用析构函数以便清除状态
placement new运算符允许构造一个给定内存中地址的对象。这允许库(例如std :: vector)为将来的构造分配一块内存。并使用placement new运算符在该块的特定位置构建类实例:void* operator new (std::size_t size, void* ptr) throw();
但是当你想删除一个对象时,你不能delete
它,因为它没有内存,但你仍然会调用析构函数,因此它会清理它自己的状态。
AFAIK 这是唯一直接调用dtor的用例
修改强> 解释为什么在最后一个对象而不是索引上调用析构函数:
std :: copy将使用它们的赋值运算符“复制”对象,如下所示:
template<class InputIterator, class OutputIterator>
OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
{
while (first!=last) {
*result = *first;
++result; ++first;
}
return result;
}
因此,对象的operator =将被多次调用。 位置索引处的对象将被写入位置索引+ 1处的对象。您希望正确的分配实现既可以释放自己的资源,也可以制作目标资源的精确副本。 完成此操作后,最终得到一个正确的数组,其中最后两个元素是彼此的精确副本,并使用析构函数清理最后一个元素。
注意:考虑到c ++ 11移动语义而不是std :: copy(如boost move算法boost::move
),可能会提高性能。
注意2:如果您在某个位置&gt;索引处持有指向列表中元素的指针,则在更改此类别后,它将指向错误的元素。