Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Исключения в конструкторе

4 views
Skip to first unread message

Polyanin Maksim

unread,
Nov 20, 2006, 11:46:14 PM11/20/06
to
Доброе утро.

Кто может указать ссылки на _место в
стандарте_, где описано поведение
исключений в конструкторе.

Есть такой класс
struct T
{
int *x;

void input()throw();

T(int *const ptr)throw():x(ptr){}
};

struct T1
{
int *a;
T b;
T2 c;

void print()const throw();

T()throw(exception&):a(0),b(0){ a=new int[10]; b.x=a;}
~T()throw(){ delete[] a; a=0; }
};

Вот его используем
try{ T obj; obj.b.input(); obj.print(); }catch(...){}

Вопросы:
1. Будет ли вызван деструктор, если
произошла ошибка в инициализаторах
конструктора T ( a(0),b(0),c() ), а
конструктор, определенный
программистом, выполняться не начинал;
2. Будет ли вызван деструктор, если
конструктор T, определенный
программистом, начал выполняться и
произошла ошибка в new.

Serguei E. Leontiev

unread,
Nov 21, 2006, 2:28:46 AM11/21/06
to
Привет, Polyanin!
Вы писали от Tue, 21 Nov 2006 04:46:14 +0000 (UTC):
PM> Кто может указать ссылки на _место в
PM> стандарте_, где описано поведение
PM> исключений в конструкторе.

На мой взгляд, твоя "подпись" (подписываться нормально надо, тогда другие
смогут нормально цитировать) не имеет отношения к исключениям. Поведение
определяется следующим положением стандарта:

3.8 Object Lifetime [basic.life]

1 The lifetime of an object is a runtime property of the object. The
lifetime of an object of type T begins

when:

- storage with the proper alignment and size for type T is obtained, and

- if T is a class type with a non-trivial constructor (12.1), the
constructor call has completed.

The lifetime of an object of type T ends when:

- if T is a class type with a non-trivial destructor (12.4), the destructor
call starts, or

- the storage which the object occupies is reused or released.


Т.е. если конструктор незавершён, то объект не создан (некоторые составные
части могли создаться), следовательно, деструктор не будет вызван (некоторые
деструкторы составных частей могут быть вызваны).

--
Успехов, Сергей Леонтьев. E-mail: l...@sai.msu.ru http://www.cryptopro.ru


Polyanin Maksim

unread,
Nov 21, 2006, 11:54:22 PM11/21/06
to
Доброе утро.

> PM> Кто может указать ссылки на _место в
> PM> стандарте_, где описано поведение
> PM> исключений в конструкторе.

SL> На мой взгляд, твоя "подпись"
Что такое "моя подпись"?

SL>не имеет отношения к исключениям.
SL> The lifetime of an object is a runtime property of the
object...begins when:
SL> The lifetime of an object of type T ends when:

SL> Т.е. если конструктор незавершён, то


объект не создан (некоторые составные

SL> части могли создаться),


следовательно, деструктор не будет
вызван (некоторые

SL> деструкторы составных частей могут
быть вызваны).
Как связаны "исключения в
конструкторе" и "время жизни объекта"?
Причина появления в этой конференции
моего вопроса очень проста:
имеющаяся у меня информация не дает
однозначного ответа на заданный мной
вопрос о том, каковы правила вызова
деструктора, если во время создания
объекта (в конструкторе) выброшено
исключение.

Некоторые отвечают на этот вопрос так:
если конструктор не был завершен
(выбросил исключение), то деструктор не
будет вызван, т.е. в конструкторе я
должен сделать что-то такое:

/*
a,b,c,d - члены объекта, которым я выделяю
в констукторе память
*/
T()
//инициализатор
:a(0),d(a), b(0),c(0)
//конструктор
{
a=alloc_mem();
if(!a)goto no_a;
b=alloc_mem();
if(!b)goto no_b;
c=alloc_mem();
if(!c)goto no_c;

//полезный код конструктора
...
if(some error)goto all_allocated;
...
if(other error)goto all_allocated;
...
return;

//дублирую в конструкторе код
деструктора
all_allocated:
free_mem(c); с=0;
no_c:
free_mem(b); b=0;
no_b:
free_mem(a); a=0;
no_a:
throw;
}
//деструктор
~T()
{
free_mem(c); с=0;
free_mem(b); b=0;
free_mem(a); a=0;
}

Другие говорят прямо противоположное:
если объект начал создаваться, то
деструктор будет вызван в любом
случае, т.е. даже если мой объект имеет
члены-указатели на встроенный тип и
инициализатор для них не был выполнен,
то деструктор попытается освободить
память, руководствуясь
неопределенными значениями этих
членов, это что-то такое:

/*
a,b,c,d - члены объекта, которым я выделяю
в констукторе память
*/
T()
//инициализатор
:a(0),
d(a), //throw - здесь прервалась работа
конструктора
b(?),c(?) //неопределены
{
}

//деструктор
~T()
{
free_mem(c); с=0;
free_mem(b); b=0; //нельзя
free_mem(a); a=0; //нельзя
}

Третьи говорят что это зависит от
реализации и стандартом не определено.

Я думаю, так. Конструктор и деструктор
нужен в С++ только для того, чтобы
выделять/освобождать память для
правильного создания объекта. Никакие
иные действия по инициализации не надо
помещать в конструктор, который к тому
же не может быть виртуальным.

С этой точки зрения выгодно разделять
коструирование объекта на
инициализацию (вызовы пар
конструктор/деструктор для членов
класса) и на собственно
конструирование (вызов
конструктора/деструктора как
специального кода определенного
именно для этого класса). Точно так же и
деструктор: на собственно деструктор
класса и на деструкторы членов класса.

Если конструктор класса (код в {}) начал
выполняться, то компилятор обязан
вызвать его деструктор, т.к. он не знает
что именно уже сделал конструктор.
Если же сбой произошел в
инициализаторе, то деструктор класса
вызывать не надо, ибо для отката
изменений надо работать с
конструторами и деструкторами членов
класса.

Если так, то достаточно придерживаться
следующей практики: "инициализируй для
безопасного вызова деструктора, а
затем спокойно пиши конструктор и
выбрасывай исключения - деструктор все
очистит правильно", чтобы не получить
"общих ошибок защиты" или утечки
памяти. Компилятор всегда знает, был ли
объект проинициализирован, т.е. стал
безопасен для вызова деструктора или
нет.

Однако, как трактует это все стандарт?

Serguei E. Leontiev

unread,
Nov 22, 2006, 2:27:11 AM11/22/06
to
Привет максим,

Polyanin Maksim wrote:
> Доброе утро.
>> PM> Кто может указать ссылки на _место в
>> PM> стандарте_, где описано поведение
>> PM> исключений в конструкторе.
> SL> На мой взгляд, твоя "подпись"
> Что такое "моя подпись"?

Мог бы посмотреть в своей почтовой программе или в интернете. Подпись,
грубо говоря, это последний абзац, некоторые программы при ответе его
просто удаляют.

> SL>не имеет отношения к исключениям.
> SL> The lifetime of an object is a runtime property of the
> object...begins when:
> SL> The lifetime of an object of type T ends when:
>
> SL> Т.е. если конструктор незавершён, то
> объект не создан (некоторые составные
> SL> части могли создаться),
> следовательно, деструктор не будет
> вызван (некоторые
> SL> деструкторы составных частей могут
> быть вызваны).
> Как связаны "исключения в
> конструкторе" и "время жизни объекта"?

Рекомендую приобрести стандарт. По
<http://webstore.ansi.org/ansidocstore/default.asp> можно приобрести
электронные копии ISO/IEC 14882:2003 (C++03) и ISO/IEC 9899:1999 (C99)
по $30 каждая.

Грубо говоря, перед вызовом любого метода класса (исключая конструкторы)
объект удовлетворяет инварианту класса. Если объект не существует, то
его деструктор не вызывается.

В случае исключения в конструкторе, вызываются деструкторы его уже
созданных компонент.

> Если так, то достаточно придерживаться
> следующей практики: "инициализируй для
> безопасного вызова деструктора, а
> затем спокойно пиши конструктор и
> выбрасывай исключения - деструктор все
> очистит правильно", чтобы не получить
> "общих ошибок защиты" или утечки
> памяти. Компилятор всегда знает, был ли
> объект проинициализирован, т.е. стал
> безопасен для вызова деструктора или
> нет.

Это слишком бедная практика, например, шаблон разработки типа:
блокировок CLock или умных указателей в него не вписываются. Но для
некоторых объектов имеет смысл её придерживаться.

--
Успехов, Сергей Леонтьев. E-mail:l...@sai.msu.ru, http://www.cryptopro.ru

Polyanin Maksim

unread,
Nov 22, 2006, 11:06:16 PM11/22/06
to
Доброе утро.

SL> На мой взгляд, твоя "подпись"

PM> Что такое "моя подпись"?
SL> грубо говоря, это последний абзац
Ничего не понял. У меня вообще нет
никакой подписи. Она абсолютно
необходима?

SL> Рекомендую приобрести стандарт.
SL> по $30 каждая.
К сожалению, не могу признать в
настоящее время такое приобретение
правильным и пока даже скачать с инета
draft нет возможности, поэтому и
спрашиваю про некоторые моменты
стандарта, может кто этот крайне
важный вопрос уже разрешил :)

SL> Грубо говоря, перед вызовом любого
SL> метода класса (исключая
конструкторы)
SL> объект удовлетворяет инварианту
класса.
SL> Если объект не существует, то
SL> его деструктор не вызывается.
Вот как раз хотел выяснить источник
такого мнения.

Просто так, из существования "времени
жизни" объекта и даже из существования
"виртуальности" деструктора прямо не
вытекает, что деструктор, как и обычный
метод класса, не может вызываться, если
конструктор выбросил исключение, т.е.
время жизни объекта не началось.

Потому что конструктор и деструктор
находятся на особом положении, по
отношению к прочим методам класса, т.к.
обычно они непосредственно
программистом не вызываются (особенно
деструктор), а управляет их вызовом
компилятор, т.е. надо просто выяснить
как предписывает стандарт.

Serguei E. Leontiev

unread,
Nov 23, 2006, 4:32:41 AM11/23/06
to
Привет, Polyanin!
Вы писали to Serguei E. Leontiev от Thu, 23 Nov 2006 04:06:16 +0000 (UTC):

SL>> На мой взгляд, твоя "подпись"
PM>> Что такое "моя подпись"?
SL>> грубо говоря, это последний абзац
PM> Ничего не понял. У меня вообще нет
PM> никакой подписи. Она абсолютно
PM> необходима?

Да.

SL>> Рекомендую приобрести стандарт.
SL>> по $30 каждая.

PM> К сожалению, не могу признать в
PM> настоящее время такое приобретение
PM> правильным и пока даже скачать с инета
PM> draft нет возможности, поэтому и
PM> спрашиваю про некоторые моменты
PM> стандарта, может кто этот крайне
PM> важный вопрос уже разрешил :)

Хм.

SL>> Грубо говоря, перед вызовом любого
SL>> метода класса (исключая

PM> конструкторы)
SL>> объект удовлетворяет инварианту
PM> класса.


SL>> Если объект не существует, то
SL>> его деструктор не вызывается.

PM> Вот как раз хотел выяснить источник
PM> такого мнения.

ISO/IEC 14882:2003(E), глава 3.8 Object Lifetime

PM> Просто так, из существования "времени
PM> жизни" объекта и даже из существования
PM> "виртуальности" деструктора прямо не
PM> вытекает, что деструктор, как и обычный
PM> метод класса, не может вызываться, если
PM> конструктор выбросил исключение, т.е.
PM> время жизни объекта не началось.

Условия вызова деструктора в это главе описано исчерпывающе.

P.S.

Здесь твоё сообщение закончилось.

--
Успехов, Сергей Леонтьев. E-mail: l...@sai.msu.ru http://www.cryptopro.ru


0 new messages