我正在编写一个可调整大小的数组类(std :: vector),作为练习,使用手动指针(因为我想在开始使用智能指针之前知道它们是如何工作的)。但是,Valgrind在checkMax()函数中报告内存泄漏。
template <typename T>
class Array{
public:
Array() : len(0),maxLen(1){
array=new T[maxLen];
// ........
}
Array(unsigned length, T&content=0) : len(length),maxLen(length*2){
array=new T[maxLen];
//..............
}
~Array(){
//delete[] array;
}
//..............
void push_back(const T& content){
checkMax();
// do stuff...
}
private:
T* array;
unsigned int len;
unsigned int maxLen;
..
void checkMax(){
if(len==maxLen){
//allocate more memory for the array
maxLen*=2;
T*temp=new T[maxLen]; // ------------- MEMORY LEAK HERE -------------
for(unsigned int i=0; i<len; i++){
temp[i]=array[i];
}
delete [] array;
array=temp;
}
}
};
我这里只发布了与内存相关的代码。
我不明白为什么Valgrind报告指定行的内存泄漏。我将旧的数组内容复制到放大的数组后删除旧数组,后两行。
另外,如果我在析构函数中取消注释delete []函数,我会得到异常double free or corruption
并且Valgrind报告无效删除(暗示重新删除),所以我完全陷入困境。
有什么想法吗?
修改 感谢您的早期回复!阅读完评论之后,我发现问题不在于我的类,而是在另一个函数中,我使用Array类作为参数调用 。如果我删除了对该函数的调用,并在类中添加了delete调用,则不会发生内存泄漏。这是我的功能:
template <typename T>
void printContents(Array<T> ar){
for(unsigned int i=0; i<ar.size(); i++){
cout<<"Content at i in array = " << ar.at(i) << endl;
}
}
在阅读了三条规则(感谢克里斯)和Grizzly发布的答案之后,我现在已经理解了删除[]无效的原因。因为我没有重载复制构造函数,所以发生了浅层复制,由于这个问题,我的数组指针已经分配到ar中的指针,而当ar超出范围时,调用了delete [],从而在主函数无效中进行删除。因此,我得到了例外。如果我删除了删除,那么数组显然会保持分配并导致内存泄漏。
感谢您的帮助,我已将Grizzly的回答投票为正确。
答案 0 :(得分:2)
通过不在析构函数中调用delete[]
来泄漏内存。这意味着在对象(对象中的“最终”数组)上最后一次调用checkMax
时分配的数组不会被调用。因此,要解决这个问题,您应该取消注释析构函数中的delete[]
正如你所提到的,这会给你一个双重免费的问题。这是基于违反三规则(在C ++ 03中它是三个规则,在C ++ 11中情况不是那么明确,但对于这种情况,三的规则足以解决你的问题)。三个规则基本上表明,当您定义自定义析构函数,复制构造函数或赋值运算符时,您需要定义所有这些。在这种情况下,您可能会在某处复制Array
。这意味着两个实例将包含相同的指针并尝试在析构函数中删除它。在同一个指针上调用两次删除是一个错误。要解决该问题,您需要定义自定义复制构造函数和赋值操作,在该操作中,您可以执行数组的深层复制(分配新内存并复制内容)。
除了所有的事情外,我个人建议从智能指针开始,然后才能更好地掌握C ++,开始尝试手动内存管理。