Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Объясните конструкцию ptr->~ClassName()


Автор: borisbn 24.3.2011, 10:57
В различном коде не часто, но встречается конструкция вида
Код

ClassName * ptr;
...
if ( ptr ) {
    ptr->~ClassName();
}

Можете объяснить назначение такого вызова ?
Правильно ли я понимаю, что delete делает примерно следующее:
Код

ClassName::delete() {
    this->~ClassName();
    free( this );
}

а ptr->~ClassName(); только вызывает деструктор, а память не освобождает ?
Если правильно, то подскажите, пожалуйста, в каких случаях это используется.
Спасибо.

Автор: azesmcar 24.3.2011, 11:04
borisbn

В C++ есть возможность создать объект в заранее выделенной области памяти.
Код

struct A {};

int main ()
{
    A* ptr = static_cast<A*>(operator new(sizeof(A)));
    new (ptr) A;
}

По сути это явный вызов конструктора. Аналогично для удаление этого объекта необходимо вызвать деструктор и освободить память.
Код

ptr->~A();
operator delete (ptr);

Автор: Modul 24.3.2011, 11:37
Код

delete pPointer;


При этом происходит не удаление указателя, а освобождение области памяти по адресу, записанному в нем. С самим указателем ничего не происходит и ему можно присвоить другой адрес. Рекомендуется указателю присваивать нулевое значение.


Цитата(borisbn @  24.3.2011,  10:57 Найти цитируемый пост)
только вызывает деструктор, а память не освобождает ?


Да. Поэтому оператор delete предпочтительнее (освобождает память и удаляет объект).

Цитата(borisbn @  24.3.2011,  10:57 Найти цитируемый пост)
подскажите, пожалуйста, в каких случаях это используется.

А что у нас обычно в деструкторе ? Что-то удаляем (объекты).

Если при создании объекта имеется динамическое распределение памяти, то выделение памяти
делают в конструкторе, ее освобождение - в деструкторе.

Т.е. для освобождения памяти и удаления объектов класса.

В конструкторе:
Код

int * pPointer = new int;


В деструкторе:
Код

delete pPointer;


А наш объект (типа ClassName) при этом не удаляем, только чистим память.

Автор: borisbn 24.3.2011, 11:58
Цитата(azesmcar @  24.3.2011,  11:04 Найти цитируемый пост)
В C++ есть возможность создать объект в заранее выделенной области памяти.

возможность то есть... а можете привети примеры, в которых это необходимо (требуется) применять ?
Другими словами, что это за случаи, когда нельзя сделать "по-человечески" new/delete ?


Цитата(Modul @  24.3.2011,  11:37 Найти цитируемый пост)
С самим указателем ничего не происходит

Цитата(Modul @  24.3.2011,  11:37 Найти цитируемый пост)
Рекомендуется указателю присваивать нулевое значение.

Цитата(Modul @  24.3.2011,  11:37 Найти цитируемый пост)
А что у нас обычно в деструкторе ? Что-то удаляем (объекты).

ты считаешь, что я этого не знаю ?

Автор: azesmcar 24.3.2011, 12:01
Цитата(borisbn @  24.3.2011,  11:58 Найти цитируемый пост)
возможность то есть... а можете привети примеры, в которых это необходимо (требуется) применять ?

Например тебе необходимо создать огромное количество мелких объектов, и чтобы не вызывать проблем с фрагментацией памяти выделяешь сразу большой кусок, в котором в дальнейшем создаешь свои объекты. Или например твоей программе требуется много памяти, и чтобы в дальнейшем избежать исключения с bad_alloc ты при запуске выделяешь столько, сколько необходимо (если есть) и работаешь уже с выделенной памятью. Примеров много, другое дело, что это не часто требуется.

Автор: Modul 24.3.2011, 12:10
Цитата(borisbn @  24.3.2011,  11:58 Найти цитируемый пост)
ты считаешь, что я этого не знаю ?


Конечно знаешь ! Просто сразу не догадался, потому спросил.  smile 


Цитата(azesmcar @  24.3.2011,  12:01 Найти цитируемый пост)
выделяешь сразу большой кусок, в котором в дальнейшем создаешь свои объекты.


А потом еще надо отдельную функцию сделать, чтобы их снова в динамике создать.
Подумалось.

Итого у нас для раздумий 2 варианта.

Автор: borisbn 24.3.2011, 12:12
azesmcar, т.е. (если я правильно понял) это используется только когда сам делашь менеджер памяти (ну или, скажем, его основной функционал).
OK, спасибо. закрываю.

Автор: azesmcar 24.3.2011, 12:14
Цитата(borisbn @  24.3.2011,  12:12 Найти цитируемый пост)
azesmcar, т.е. (если я правильно понял) это используется только когда сам делашь менеджер памяти (ну или, скажем, его основной функционал).

Не только, скорее в основном. Явный вызов деструктора нужен тогда, когда ему предшествовал явный вызов конструктора. Еще один пример использования можешь посмотреть у Александреску - http://books.google.am/books?id=aJ1av7UFBPwC&pg=PA137&lpg=PA137&dq=phoenix+singleton+Alexandrescu&source=bl&ots=YRaD3sWc4Y&sig=j32rbRbdx2GRMrA8AxAHJWYWgpI&hl=en&ei=UguLTZysJpGwhQfJiImrDg&sa=X&oi=book_result&ct=result&resnum=1&ved=0CBIQ6AEwAA#v=onepage&q&f=false.

В общем-то это возможность языке, а где и как ее применять личное дело каждого.

Цитата(Modul @  24.3.2011,  12:10 Найти цитируемый пост)
А потом еще надо отдельную функцию сделать, чтобы их снова в динамике создать.

не совсем понял о чем речь.

Автор: Modul 24.3.2011, 12:19
Цитата(azesmcar @  24.3.2011,  12:14 Найти цитируемый пост)
не совсем понял о чем речь.


Если мы дальше хотим работать с объектом, 
снова надо эти мелкие объекты (может массивы) создать после их удаления в деструкторе
в отдельной функции.

Автор: bsa 24.3.2011, 12:29
Цитата(Modul @  24.3.2011,  12:19 Найти цитируемый пост)
Если мы дальше хотим работать с объектом, 
снова надо эти мелкие объекты (может массивы) создать после их удаления в деструкторе
в отдельной функции.
Че?

Посмотрите реализацию std::vector. Там это используется - reserve() выделяет блок памяти, resize() создает объекты в уже выделенной области.

Автор: Modul 24.3.2011, 12:38
bsa, я про такой вариант:

Код

int * pPointer = new int;


Думаю, может меня остальные не поняли.

При создании объекта имеется динамическое распределение памяти (мелкие объекты), выделение памяти сделали в конструкторе, ее освобождение - в деструкторе.

Вызвали явно деструктор, мелкие объекты удалили. 

При дальнейшей работе с нашим объектом (его не удалили) эти мелкие динамические объекты снова создаем через отдельный метод.

Автор: bsa 24.3.2011, 13:49
Modul, я опять не понял, о чем ты говоришь. Если об освобождении ресурсов деструктором, которые выделил конструктор, то это и ежу понятно. Причем не важно, выделялись ли они на самом деле или нет. Если сконструировал объект, то изволь его затем деконструировать. И не важно, int это, или мега сложный класс управления стержнями ядерного реактора.

Автор: borisbn 24.3.2011, 14:18
bsa, хммм. Действительно, каждый день пользую, а не подумал про вектор. Там, пожалуй, по-другому и не сделаешь.

Автор: Modul 24.3.2011, 14:24
Цитата(Modul @  24.3.2011,  12:10 Найти цитируемый пост)
Итого у нас для раздумий 2 варианта.


1) delete myObject;

Цитата(bsa @  24.3.2011,  13:49 Найти цитируемый пост)
Если сконструировал объект, то изволь его затем деконструировать. 


С этим всем ясно ! smile 

2) По сабжу собственно:

1. создали объект

ClassName *myObject = new ClassName;

В конструкторе у нас были мелкие динамические объекты, типа:
Код

int * pPointer = new int;



2. вызвали явно деструктор
myObject->~ClassName();

и удалили все наши мелкие динамические объекты. 
delete pPointer;

Объект myObject у нас существует - занимает память, а наши мелкие динамические объекты удалены.

3. Для новой работы с объектом myObject требуется создать снова наши мелкие динамические объекты,
созданные в конструкторе (например, pPointer).
Для этого я предлагал сделать отдельную функцию.

И шото меня не поняла общественнось. smile 

Автор: bsa 24.3.2011, 16:02
Цитата(Modul @  24.3.2011,  14:24 Найти цитируемый пост)
И шото меня не поняла общественнось. 

Да потому что ты какую-то ерунду сморозил. Назови хоть один пример, когда требуется делать так, как ты рекомендуешь?

Непосредственный вызов деструктора используется ТОГДА И ТОЛЬКО ТОГДА, когда использовался размещающий new. Так как этот оператор просто вызывает конструктор, передавая ему в качестве указателя this адрес заданной тобой области памяти. Используется это для создания так называемых пулов - собственных "куч".
Пример:
Код
class X;
char *pool = new char[sizeof(X) * 1000];
...
X *p = new (pool + sizeof(X) * 100)(12); //создать объект класса X по адресу pool + sizeof(X) * 100, и передать его конструктору 12
...
p->~X(); //уничтожить объект
...
delete []pool;

Автор: Modul 24.3.2011, 16:49
Привожу мою ерунду:

My.h

Код

#ifndef _My_h_
#define _My_h_

class My
{ 
    public:
        My();
        ~My();
        void setNumber();
        void getNumber(); 
        void create();

    private:
        int *pPointer;

};

#endif


My.cpp

Код

#include "My.h"
#include <iostream>


// consrtuctor
My::My()
{
    pPointer = 0;
    pPointer = new int;

    // ...еще много всего может быть...
}

// destructor
My::~My()
{
    delete pPointer;
    pPointer = 0;
}


void My::setNumber()
{
    *pPointer = 66;
}


void My::getNumber()
{
    std::cout << *pPointer << std::endl;
}


// create pPointer
void My::create()
{
    if(pPointer == 0)
        pPointer = new int;
}


main.cpp

Код

#include "My.h"

int main()
{

    My *myObject = new My;
    myObject->setNumber();
    myObject->getNumber();
    
    myObject->~My();

    myObject->create();
    myObject->setNumber();
    myObject->getNumber();


    return 0;
}

Автор: bsa 24.3.2011, 16:55
Я и без примеров прекрасно понял о чем речь. Я не понял, ЗАЧЕМ? Назови хоть одну причину, по которой нужно городить такой неочевидный и опасный код? Какова вероятность, что человек не забудет вызывать деструктор перед вызовом create()? И если тебе нужно проинициализировать заново объект, то кто мешает использовать operator= или, если совсем критично по скорости, конструкцию My().swap(myObject)?

Автор: Modul 24.3.2011, 17:06
Цитата(bsa @  24.3.2011,  16:55 Найти цитируемый пост)
Я не понял, ЗАЧЕМ? 


Это по сабжу, что так можно.


Цитата(bsa @  24.3.2011,  16:55 Найти цитируемый пост)
Назови хоть одну причину, по которой нужно городить такой неочевидный и опасный код? 


Например, удаление (очистка) массы этих переменных.

Ну, дорогой bsa, Вы же сами прекрасный программист.

И степень опасности он же и определяет. Все в его ровных (или кривых) руках.

p.s. Так никогда сам не делал, это к сабжу все.

Автор: mes 24.3.2011, 20:00
Цитата(Modul @  24.3.2011,  16:06 Найти цитируемый пост)
Это по сабжу, что так можно.

хороший пример, как ни в коем случае нельзя делать... 
 smile 

 

Автор: Modul 24.3.2011, 20:46
"... а кто знает, тот не говорит" - народная китайская поговорка.


Цитата(mes @  24.3.2011,  20:00 Найти цитируемый пост)
как ни в коем случае нельзя делать... 


Расскажите почему, просим...

Автор: mes 24.3.2011, 21:22
Цитата(Modul @  24.3.2011,  19:46 Найти цитируемый пост)
Расскажите почему, просим... 

главный ответ тут :
Цитата(bsa @  24.3.2011,  15:02 Найти цитируемый пост)
Непосредственный вызов деструктора используется ТОГДА И ТОЛЬКО ТОГДА, когда использовался размещающий new. 


еще нужно вспомнить о правиле хорошего тона, о соблюдении симметрии создания и удаления.. 

ну и как результат в вашем примере будет либо двойной вызов деструктора, либо память не будет освобождена.. 






Автор: Modul 24.3.2011, 21:42
Цитата(mes @  24.3.2011,  21:22 Найти цитируемый пост)
будет либо двойной вызов деструктора


Т.е. мы один раз удалили при удалении в деструкторе, 
а потом при удалении нашего объекта myObject  еще раз.

Автор: mes 24.3.2011, 21:55
Цитата(Modul @  24.3.2011,  20:42 Найти цитируемый пост)
Т.е. мы один раз удалили при удалении в деструкторе, 
а потом при удалении нашего объекта myObject  еще раз.

один раз  Вы вызвали деструктор вручную,
а второй раз вызовется при выходе за границы пользования (для стекового и статического объектов)
или при вызове delete (для  объекта кучи)

обратите внимание на пример bsa.. память в которой размещается объект имеет тип char -т.е. пустой буфер, при удалении которого никаких деструкторов не надо.. 

Автор: borisbn 24.3.2011, 21:55
Цитата(Modul @  24.3.2011,  17:06 Найти цитируемый пост)
Это по сабжу, что так можно.

как ТС вставлю своё слово...
вообще-то сабж был не "можно ли это" а 
Цитата(borisbn @  24.3.2011,  10:57 Найти цитируемый пост)
подскажите, пожалуйста, в каких случаях это используется.

Цитата(borisbn @  24.3.2011,  11:58 Найти цитируемый пост)
можете привети примеры, в которых это необходимо (требуется) применять ?

Цитата(borisbn @  24.3.2011,  11:58 Найти цитируемый пост)
Другими словами, что это за случаи, когда нельзя сделать "по-человечески" new/delete ?


Добавлено через 1 минуту и 58 секунд
вообще-то, я уже всё понял, поэтому отвечать на эти три вопроса не нужно smile

Автор: Modul 24.3.2011, 22:06
Спасибо, borisbn, за Вашу тему !

Поэтому, она позволили не  только Вам найти ответ на Ваш вопрос, 
но и мне и другим людям в сообществе.

А плохо это или хорошо, это решать программисту. Спасибо большое всем !

Автор: bsa 24.3.2011, 23:20
Цитата(Modul @  24.3.2011,  22:06 Найти цитируемый пост)
А плохо это или хорошо, это решать программисту.

К сожалению. С++ позволяет слишком многое, чем "пользуются" недостаточно опытные программисты, не знающие, чем это черевато.

Автор: mes 24.3.2011, 23:25
Цитата(Modul @  24.3.2011,  21:06 Найти цитируемый пост)
А плохо это или хорошо, это решать программисту. 

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

Автор: Modul 24.3.2011, 23:38
Философские мысли:

Как ужасно спится манагеру, который продает ужасную программу г....но клиенту с г...кодом.  smile 

который приходит в 14-15 часов и говорит, 
ну что...клиент убегает,
нас увольняют,
закрывают...

Потом приходит и говорит после 5 мин. базара,  опа, 

А как мы вчера ДУНУЛИ !!!  smile 


Надо спросить Бориса.

Автор: borisbn 24.3.2011, 23:40
Цитата(Modul @  24.3.2011,  23:38 Найти цитируемый пост)
А как мы вчера ДУНУЛИ !!!

верю  smile 

Автор: alexvs11 24.3.2011, 23:47
первое что приходит в голову - вызов деструктора, чтобы по этому же адресу вызвать явно конструктор

так можно заново проинициализировать объект, при этом все внешние ссылки на этот объект останутся валидными

Автор: bsa 25.3.2011, 13:22
Цитата(alexvs11 @  24.3.2011,  23:47 Найти цитируемый пост)
первое что приходит в голову - вызов деструктора, чтобы по этому же адресу вызвать явно конструктор

так можно заново проинициализировать объект, при этом все внешние ссылки на этот объект останутся валидными

Более безопасный способ:
Код
MyClass().swap(myObject);

Автор: alexvs11 25.3.2011, 22:06
bsa, ну можно и operator=, но так в сам класс не требуется вносить никаких изменений, те удобно для несвоих классов
хотя опасно-опасно

Автор: bsa 25.3.2011, 22:53
operator=() в общем случае работает дольше, так как реализуется:
Код
MyClass& MyClass::operator=(const MyClass &src)
{
   MyClass(src).swap(*this);
   return *this;
}

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)