2013-05-25 1 views
2

Я пишу повторно массивный класс массива (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 сообщает о недопустимом удалении (подразумевая повторное удаление), поэтому я полностью смущен. Любые идеи?

EDIT: Благодарим за ответы на вопросы! Прочитав комментарии, я обнаружил, что проблема связана не с моим классом, а с другой функцией, которую я вызывал с классом Array в качестве аргумента. Если я удалю вызов этой функции и добавит вызов удаления в класс, утечки памяти не произойдет. Вот моя функция:

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 [], тем самым основная функция недействительна. Следовательно, я получал исключение. Если бы я удалил удаление, тогда массив, очевидно, останется выделенным и приведет к утечке памяти.

Спасибо за помощь, я проголосовал за ответ Гризли как правильный.

+1

http: // stackoverflow.com/questions/4172722/what-is-the-rule-of-three – chris

+0

Чтобы разработать правило трех комментариев, копируете ли вы из одного массива в другой, что может привести к двойному удалению? –

+0

@chris Да, это было то, о чем я тоже думал. Сумант, вы передаете «Массив» по ​​значению в качестве параметра в любом месте или создаете копии по-другому? У вас есть конструктор копирования, который правильно обрабатывает указатель 'array'? Если вам не хватает конструктора копирования, поведение по умолчанию было бы просто скопировать указатель, а это значит, что у вас есть два указателя на один и тот же «массив», следовательно, двойное удаление. – wilsonmichaelpatrick

ответ

2

Вы теряете память, не вызывая delete[] в деструкторе. Это означает, что массив, который выделяется при вызове checkMax в последний раз на объект («конечный» массив в объекте), не вызывается никогда. Поэтому, чтобы решить, что вы должны раскомментировать delete[] в деструкторе

Как вы упомянули, это даст вам проблему с двойным бесплатным. Это основано на нарушении правила трех (хорошо в C++ 03 это правило из трех, в C++ 11 ситуация не так понятна, но для этого сценария правило три достаточно для решить вашу проблему). Правило три в основном утверждает, что при определении настраиваемого деструктора, конструктора копирования или оператора присваивания вам необходимо определить все из них. В этом случае вы скорее всего сделаете копию Array. Это означает, что оба экземпляра будут содержать один и тот же указатель и попытаться удалить его в деструкторе. Ошибка вызова по тому же указателю дважды - ошибка. Чтобы решить эту проблему, вам нужно определить собственный экземпляр-конструктор и назначение op, в котором вы делаете глубокую копию массива (выделяете новую память и копируете содержимое ).

В любом случае, я бы посоветовал начать с smartpointers, и только позже, когда у вас будет лучшее сцепление с C++, начните экспериментировать с ручным управлением памятью.

+1

Хороший ответ, но я не согласен с последней точкой вашего сообщения, я думаю, что ручное управление памятью следует понимать, когда вы учитесь, поскольку цель состоит в понимании, а не в том, чтобы что-то работать. (И мы, наверное, все узнали об этом, сделав именно то, что плакат только что сделал в какой-то момент!) – wilsonmichaelpatrick

+0

@wilsonmichaelpatrick: Я не утверждаю, что никогда не следует изучать ручное управление памятью, просто не следует начинать с этого (как это делает ОП). C++ достаточно сложно, даже если не учитывать ручное управление памятью, а интеллектуальные указатели не будут волновать все проблемы, а просто служат для того, чтобы сделать их более очевидными ('unique_pointer' просто запретил бы копию, непосредственно связавшись с вопросом о том, почему и что делать) – Grizzly