Доброго всем времени суток, читая уважаемого С.Майерса возник вот такой вопрос.
Во второй части эпохального труда "Effective C++", в правиле 26 дается пример класса с ограничением числа экземпляров, которое обеспечивается за счет генерации исключений в конструкторе, примерно вот так:
Foo::Foo()
{
if (_num_instances >= NUM_INSTANCES_MAX)
throw TooManyInstances();
}
Предполагается, что в случае, если будет сделана попытка создать слишком много экземпляров класса, возникшее исключение не позволит довести процесс до конца. Если исключение не будет перехвачено, то приложение просто вылетит, если будет перехвачено, но экземпляр Foo будет создаваться в стеке — за счет выравнивания стека выделенная для экземпляра память будет возвращена системе. Однако что будет в случае, когда экземпляр создается в куче, а исключение перехватывается? Перед вызовом конструктора будет вызван new и выделена память, в конструкторе будет сгенерировано исключение, вызывающий код его перехватит, а выделенная память — так и останется висеть, и приложение в принципе не может получить указатель на эту память и уж тем более освободить ее. Так? Или стандарт как-то эту ситуацию оговаривает особо? Или я чего-то недопонимаю?
Заранее благодарю за разъяснения.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Предполагается, что в случае, если будет сделана попытка создать слишком много экземпляров класса, возникшее исключение не позволит довести процесс до конца. Если исключение не будет перехвачено, то приложение просто вылетит, если будет перехвачено, но экземпляр Foo будет создаваться в стеке — за счет выравнивания стека выделенная для экземпляра память будет возвращена системе. Однако что будет в случае, когда экземпляр создается в куче, а исключение перехватывается? Перед вызовом конструктора будет вызван new и выделена память, в конструкторе будет сгенерировано исключение, вызывающий код его перехватит, а выделенная память — так и останется висеть, и приложение в принципе не может получить указатель на эту память и уж тем более освободить ее. Так? Или стандарт как-то эту ситуацию оговаривает особо? Или я чего-то недопонимаю?
_>Заранее благодарю за разъяснения.
Объект не удалось сконструировать, значит память не выделена. Ничего освобождать не надо.
Здравствуйте, slava_phirsov, Вы писали:
_>Перед вызовом конструктора будет вызван new и выделена память, в конструкторе будет сгенерировано исключение, вызывающий код его перехватит, а выделенная память — так и останется висеть, и приложение в принципе не может получить указатель на эту память и уж тем более освободить ее. Так? Или стандарт как-то эту ситуацию оговаривает особо? Или я чего-то недопонимаю?
В таком случае память будет освобождена автоматически. Если из конструктора объекта вылетает исключение, то вызывается соответствующий delete.
Здравствуйте, remark, Вы писали:
R>В таком случае память будет освобождена автоматически. Если из конструктора объекта вылетает исключение, то вызывается соответствующий delete.
Ссылку на пункт в стандарте дай, плиз, если помнишь. Не то чтобы я тебе совсем не доверял, но просто самому интересно лишний раз перечитать.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Здравствуйте, remark, Вы писали:
R>>В таком случае память будет освобождена автоматически. Если из конструктора объекта вылетает исключение, то вызывается соответствующий delete.
_>Ссылку на пункт в стандарте дай, плиз, если помнишь. Не то чтобы я тебе совсем не доверял, но просто самому интересно лишний раз перечитать.
Не помню. Должно быть там, где про new/delete для объектов.
Здравствуйте, slava_phirsov, Вы писали:
_>за счет выравнивания стека выделенная для экземпляра память будет возвращена системе.
за счет чего-чего?
_>Или стандарт как-то эту ситуацию оговаривает особо?
Оговаривает. Память будет освобождена автоматически.
Здравствуйте, Igore, Вы писали:
I>Объект не удалось сконструировать, значит память не выделена. Ничего освобождать не надо.
Это не так.
Сначала выделяется память, потом в ней констурируется объект.
Если не получилось сконструировать — память освобождается автоматически (точнее, автоматически зовется deallocation function).
К уже сказанному можно добавить, что корректное освобождение памяти, выделенной new, при исключении из конструктора — это единственная причина, почему при написании своей версии оператора new следует написать и свою версию оператора delete. Компилятором неявно вызывается особая форма оператора delete только при исключении из конструктора, стейтмент delete p; — это деструктор и стандартный delete.
Здравствуйте, slava_phirsov, Вы писали:
_>Здравствуйте, remark, Вы писали:
R>>В таком случае память будет освобождена автоматически. Если из конструктора объекта вылетает исключение, то вызывается соответствующий delete.
_>Ссылку на пункт в стандарте дай, плиз, если помнишь. Не то чтобы я тебе совсем не доверял, но просто самому интересно лишний раз перечитать.
Здравствуйте, Alexander G, Вы писали:
AG>Здравствуйте, slava_phirsov,
AG>К уже сказанному можно добавить, что корректное освобождение памяти, выделенной new, при исключении из конструктора — это единственная причина, почему при написании своей версии оператора new следует написать и свою версию оператора delete. Компилятором неявно вызывается особая форма оператора delete только при исключении из конструктора, стейтмент delete p; — это деструктор и стандартный delete.
мне кажется, что вы сморозили чушь
рекомендую перечитать 5.3.4 (new) и 5.3.5 (delete)
в двух словах:
5.3.4.8:
A new-expression obtains storage for the object by calling an allocation function (3.7.3.1) If the new-expression terminates by throwing an exception, it may release storage by calling a deallocation function (3.7.3.2)
5.3.5.7
The delete-expression will call a deallocation function.
Allocation functions — это operator new(), operator new[]() причем implementation provides default definitions of the global allocation functions
Deallocation functions — это operator delete(), operator delete[]() причем implementation provides default definitions of the global deallocation functions
если написать ::new A(), то используется глобальная функция аллокации, если
new A(), то функция operator new() сначала ищется в скоупе A, затем в глобальном скоупе
вывод:
"при написании своей версии оператора new следует написать и свою версию оператора delete" для того, чтобы не было глюков и память очищалась правильно
Здравствуйте, uzhas, Вы писали:
AG>>К уже сказанному можно добавить, что корректное освобождение памяти, выделенной new, при исключении из конструктора — это единственная причина, почему при написании своей версии оператора new следует написать и свою версию оператора delete.
U>мне кажется, что вы сморозили чушь
да.
я имел ввиду
почему при написании своей формы оператора new следует написать и свою форму оператора delete.
т.е. допустим
void* opertor new(size_t size, mytag& tag);
void opertor delete(void* p, mytag& tag); // вызыается только либо явно,
// либо при исключении в конструкторе после вызова new выше.
mytag tag;
X * x = new (tag) X();
delete x; // стандартный operator delete, а не своя форма.delete (tag) x; // такого не бывает.
Здравствуйте, uzhas, Вы писали:
U>A new-expression obtains storage for the object by calling an allocation function (3.7.3.1) If the new-expression terminates by throwing an exception, it may release storage by calling a deallocation function (3.7.3.2)
may release, или should release? Глупо, конечно, цепляться к словам, но все же стандарт — это Документ. Слово may вызывает у меня смутные сомнения
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)