Теория (delete)
TFileStream *stream = new TFileStream(OpenDialog1->FileName,fmOpenRead);
//использую
stream->~TFileStream();
TFileStream *stream = new TFileStream(OpenDialog1->FileName,fmOpenRead);
//использую
delete stream;
Это не мой код (первая часть). Я увидела его в другом приложении. Приложение работает. Возник вопрос: деструктор в этом случае освободит память?
Я всегда использую delete. Мне просто интересно с теоретической точки зрения. Первый код правильный?
TFileStream *stream = new TFileStream(OpenDialog1->FileName,fmOpenRead);
//использую
stream->~TFileStream();
TFileStream *stream = new TFileStream(OpenDialog1->FileName,fmOpenRead);
//использую
delete stream;
Это не мой код (первая часть). Я увидела его в другом приложении. Приложение работает. Возник вопрос: деструктор в этом случае освободит память?
Я всегда использую delete. Мне просто интересно с теоретической точки зрения. Первый код правильный?
конечно правильный.
но. применять явный вызов деструктора без надобности по меньшей мере странно.
применение этой вещи - placement new. то бишь размещение объекта в другой области памяти.
размещаем в куче память через new. а потом в этом размещении рожаем объект того типа, который нам нужно
цитирую:
Unlike ordinary new, placement new takes an additional argument: the memory address on which the object will be constructed. Since placement new doesn't really allocate storage, it's faster and safer than ordinary new. Of course, the programmer must guarantee that the pre-allocated storage is sufficiently large and that it meets the system's alignment requirements.
To meet these criteria, use ordinary new to allocate raw storage. Then use placement new to construct the desired object on the previously allocated storage.
char * raw_mem = new char [sizeof (Timer)];
...
C++ guarantees that the memory returned from new meets the strictest alignment requirements for every type of object. Therefore, you can use a dynamically allocated char array as the basis for constructing any other type of object
char * buff [sizeof (Timer)]; //bad idea
...
#include <new>
Timer * ptimer = new (raw_mem) Timer;
....
Objects allocated by placement new require an explicit destructor invocation once they aren't needed. Don't use delete or delete[] to destroy them as this would cause undefined behavior:
delete ptimer; // bad idea
The problem is that delete knows which object new allocated originally, namely a char array. It doesn't know that a different object was later constructed on that array. Therefore, it won't invoke Timer's destructor. Worse yet, because the original buffer was allocated using new[], you must use delete[] to release it. Here's how you invoke Timer's destructor:
ptimer->~Timer(); //explicit destructor invocation
Во втором случае деструктор тоже вызовется, а потом еще и удалиться память выделенная под объект.
Что курил автор прицитированного английского текста ?????
Деструктор сам объект не удаляет, и уж тем более, не должен - иначе получим кучу глюков при повторном удалении.
Путать delete и delete[], а также free не рекомендуется - CodeGuard предупреждает.
Если требуется выделить память, используйте функцию malloc. Выделенную память требуется освобождать только через free.
прошу обосновать предпосылки к куреву :))))
никто и не путает delete и delete[]
насчет функции malloc, боюсь мы начнем спорить о разности подходов.
предлагаю автору переписать/дополнить википедию.
из оттуда:
Дестру́ктор — специальный метод класса, служащий для удаления объекта из памяти.
по Гради Бучу: Деструктор - Операция класса, которая освобождает состояние объекта и/или уничтожает сам объект.
по Страуструпу: Можно описать специальную функцию-член
для удаления объектов класса при его уничтожении. Такая
функция называется деструктором.
автор против всего мира ?
{
private:
int* i;
public:
A () : i (new int)
{
}
~A ()
{
delete i;
}
};
int main ()
{
A a;//все хорошо. в конструкторе выделели, в деструкторе освободили.
A* aa = new A;//выделили в конструкторе и освободили в деструкторе память под int.
aa->~A (); //НО память под A выделенную тут мы не освобождаем.
}
Таким образом первый код это утечка памяти.
Деструктор удаляет объект, но не освобождает память.
Собственно, lena_ki уже сама разобралась... :)
у Страуструпа глава 6.7.2 Указание размещения
и там явный вызов деструктора.
остальное, мне кажется, от лукавого
Я проблем не создовала. Просто задала вопрос по теории. :)
по Страуструпу: Можно описать специальную функцию-член
для удаления объектов класса при его уничтожении. Такая
функция называется деструктором.
А что понимается под "объектом класса": объекты этого класса, или поля класса? Не знаю, что имел в виду сам Страуструп, но компиляторы C++ используют только второе трактование :D
Правда, при большом желании, можно создать функцию, которая будет удалять объект, освобождая занятую им память: operator delete (TMyClass *Object);
Если мыслить логически, то можно найти мегапричину, не позволяющую деструктору освобождать память, занятую объектом:
1. Объект может быть полем другого объекта - таким образом, занимаемая им память будет принадлежать объекту-владельцу, со всеми вытекающими последствиями по части выделения/освобождения памяти.
2. Объект может быть локальным - расположенным в стеке программы - а эта область памяти вообще не выделяется и не освобождается при работе приложения.
Как видно, освобождение памяти находится вне компетенции деструктора. Для этого есть два оператора (delete и delete[]) и две функции (free и realloc (Адрес, 0)), которые вызываются только для самостоятельных объектов, расположенных в "куче".
P.S. Насчёт "курения":
//....
Timer * ptimer = new (raw_mem) Timer;
На трезвую голову такое придумать сложно :D . Адекватные люди, если не любят new :confused: , пишут malloc и не балуются "промежуточными" типами.
//....
Timer* ptimer = new (raw_mem) Timer;
или даже так:
//....
Timer* ptimer = new (raw_mem) Timer;
Тем более, что в первом примере потом будет сложно удалять
delete [] (char*) pTimer;
А чем второй пример лучше простого new Timer(), записанного в одну строку ;) ?
Конечно, ручной вызов деструкторов и конструкторов тоже имеет своё применение - но такие случаи редко бывают.
P.S. Ох, запутаем сейчас начинающих.... А им лучше всего запомнить "главное правило" - не усложнять :D
Объясняю. После выделения памяти под объект и перед конструированием самого объекта может пройти некоторое время, выполниться другие необходимые операции. Аналогично после удаления объекта и перед освобождением памяти.
Например, контейнер std::vector. Как известно память в нем выделяется с запасом, и объекты конструируются уже в выделенной памяти.
Другой пример: пакет определенной длины, в котором могут содержаться различного типа данные.
Ещё пример: структуры переменной длины:
{
int size;
char content[1];
};
В своих программах мне порою тоже приходилось реализовывать механизмы выделения/освобождения памяти. Но я использовал более традиционные функции malloc, realloc и free.
Точно сказать не могу, надо искать ответ.
Но могу предположить:
1) для единой стилистики в программах на С++, malloc - это все же C, а реализация new может быть и не завязана на malloc;
2) неудачный malloc возвращает 0, а вот неудачный new в зависимости от настроек компиляции может как вернуть 0, сгенерировать исключение или вызвать специальную callback-функцию new_handler, которая устанавливается с помощью set_new_handler.
IMHO, отличное предположение.