2010-11-02 3 views
4

Можно создать дубликат:
How could pairing new[] with delete possibly lead to memory leak only?удаление массиву неправильного пути

Я всегда говорил, что это не безопасно для вызова удалять на массиве, наделенном новое []. Вы всегда должны сочетать новые с delete и new [] с delete [].

Так что я был удивлен, обнаружив, что следующий код компилируется и работает нормально, как в режиме отладки, так и в режиме Release под VS2008.

class CBlah 
{ 
public: 
    CBlah() : m_i(0) {} 

private: 
    int m_i; 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    for(;;) 
    { 
     CBlah * p = new CBlah[1000]; // with [] 
     delete p;     // no [] 
    } 
    return 0; 
} 

Это мне потребовалось некоторое время, чтобы выяснить, почему это работает на всех, и я думаю, что это просто везение, а некоторые неопределенное поведение.

BUT ... это заставило меня задуматься ... почему Visual Studio не забирает это, по крайней мере, в диспетчере памяти Debug? Это потому, что есть много кода, который делает эту ошибку, и они не хотят ее нарушать, или они чувствуют, что задача менеджера памяти Debug не в том, чтобы поймать такую ​​ошибку?

Любые мысли? Является ли это распространенным распространением?

+0

Это логическая ошибка, которая не может быть обнаружена компиляторами. Однако хорошие IDE могли бы выявить эти. – jwueller

+0

Этот вопрос http://stackoverflow.com/questions/1913343/how-could-pairing-new-with-delete-possible-lead-to-memory-leak-only подробно объясняет все детали. И да, это типичная ошибка. – sharptooth

+2

@Hans Passant: Нет, массив не просочился, это UB. Я связался с вопросом, который подробно объясняет всю внутреннюю работу. – sharptooth

ответ

3

Это неопределенное поведение и все в любви, войне и непредсказуемое поведение ... :)

-1

Это работает, потому что особенности ++ библиотека времени выполнения C она была связана с использует ту же кучу, как для operator new и operator new[]. Многие это делают, но некоторые этого не делают, поэтому практика не рекомендуется.

Другая большая разница в том, что если CBlah был нетривиальный деструктор, то delete p; бы только назвать это первый объект в массиве, в то время как delete[] p; обязательно называть его для всех объектов.

+0

На самом деле это вас не сильно беспокоит: http://stackoverflow.com/questions/2245196/c-urban-myths/2245208#2245208 – sharptooth

+0

Это UB - все может случиться. –

+0

OP говорит, что какой-то конкретный код «работает нормально» [или кажется]. Даже если это так, что есть некоторая куча коррупции, которая еще не закончилась, все равно верно, что любой деструктор для удаленного типа будет вызываться только один раз с помощью 'delete p'. – aschepler

9

Это, конечно, скомпилировать нормально, потому что нет никакой информации в указателе (во время компиляции), который будет видеть, если указатель указывает на массив или какие. Например:

int* p; 

cin>>x; 
if(x == 0) 
    p = new int; 
else 
    p = new int [10]; 

delete p; //correct or not? :) 

Теперь, о работе ОК. Это называется неопределенным поведением в C++, то есть нет никакой гарантии, что произойдет - все может работать нормально, вы можете получить segfault, вы можете получить просто неправильное поведение, или ваш компьютер может решить позвонить по телефону 911. UB < => нет гарантии

+1

Да. На практике либо он работает, либо развращает кучу. И куча коррупции * очень плохо *. – sharptooth

+1

Он будет скомпилирован, также не гарантируется. В основном это UB. Нет правил. – Chubsdad

+0

@Chubsdad Компиляция в этом гарантируется (по крайней мере, практически), не так ли? Потому что он никогда не узнает, указывает ли указатель на массив или что. В общем, верно, UB включает в себя сбой компиляции, он включает в себя все на самом деле. –

1

Согласно MDSN, при удалении массива он переводит delete, чтобы удалить []. (См., Например, there). Хотя вы должны иметь предупреждение после компиляции.

1

Причина, по которой Debug Memory Manager не воспринимает эту ошибку, вероятно, потому, что она не реализована на уровне нового/удаления, но на уровне менеджера памяти, который вызывается по new/delete to выделите требуемую память.
В этот момент различие между новым массивом и скалярным новым исчезло.

+0

Фактически в Visual C++ 'new []' always calls' operator new []() 'и' delete' всегда вызывает 'operator delete()', но они просто реализован таким же образом - через 'malloc()'. Никто не заботится и по какой-то причине - ловить все возможные ошибки в программе на C++ не является задачей отладки CRT. – sharptooth

0

Я не знаю, что заставляет вас думать, что это «работает нормально». Он компилируется и завершается без сбоев.Это не означает, что не было никакой утечки или повреждения кучи. Кроме того, если на этот раз вам это удастся, это не обязательно сделает его безопасным.

Иногда даже переписывание буфера - это то, что вам «сойдет с рук», потому что байты, которые вы написали, не были использованы (возможно, это дополнение для выравнивания). Тем не менее, вы не должны обойти это.

Кстати, новый T [1] является формой нового [] и по-прежнему требует удаления [], хотя в этом случае есть только один элемент.

0

Интересная точка. Как только я просмотрел код и попытался убедить программистов исправить новое [] - удалить несоответствие. Я аргументировал себя «Элементом 5» от Effective C++ Скоттом Мейерсом. Тем не менее, они аргументировали словами: «Что вы хотите, это хорошо работает!» и доказал, что утечки памяти не было. Однако он работал только с POD-типами. Похоже, MS пытается исправить несоответствие, как указал Равелин.

Что произойдет, если вы добавите деструктор?

#include <iostream> 
class CBlah 
{ 
    static int instCnt; 
public: 
    CBlah() : m_i(0) {++instCnt;} 
    ~CBlah() 
    { 
     std::cout <<"d-tor on "<< instCnt <<" instance."<<std::endl; 
     --instCnt; 
    } 
private: 
    int m_i; 
}; 

int CBlah::instCnt=0; 

int main() 
{ 
    //for(;;) 
    { 
     CBlah * p = new CBlah[10]; // with [] 
     delete p;     // no [] 
    } 
    return 0; 
} 

Какое бы глупое исправление «интеллект» не было добавлено в VS, код не переносится.

0

Помните, что «работает правильно» находится во вселенной «неопределенного поведения». Вполне возможно, что конкретная версия конкретного компилятора реализовала это так, чтобы она работала во всех смыслах и целях. Важно помнить, что это не гарантируется, и вы действительно не можете быть уверены, что он работает на 100%, и вы не можете знать, что он будет работать со следующей версией компилятора. Он также не переносится, поскольку другой компилятор может работать по-другому.

Смежные вопросы