Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.
Здравствуйте, 0x7be, Вы писали:
0>Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.
А вдруг конструктор успел захватить какие-то unmanaged ресурсы до того как кинул исключение. Если не вызывать финализатор, то кто будет их освобождать?
Здравствуйте, nikov, Вы писали:
N>А вдруг конструктор успел захватить какие-то unmanaged ресурсы до того как кинул исключение. Если не вызывать финализатор, то кто будет их освобождать?
Пускай он их и освобождает. Мне более правильно кажется логика конструирования/разрушения принятая в C++.
Деструкторы там вызываются только для полностью сконструированных объектов (подобъектов). Ошибки в процессе конструирования должны обрабатываться самим конструктором.
Re[3]: Исключение в конструкторе + финализатор
От:
Аноним
Дата:
07.07.10 14:25
Оценка:
Здравствуйте, 0x7be, Вы писали:
0>Здравствуйте, nikov, Вы писали:
N>>А вдруг конструктор успел захватить какие-то unmanaged ресурсы до того как кинул исключение. Если не вызывать финализатор, то кто будет их освобождать? 0>Пускай он их и освобождает. Мне более правильно кажется логика конструирования/разрушения принятая в C++.
А почему вам так кажется? Финализатор в любом случае принято писать устойчивым к повторном вызовам.
Здравствуйте, 0x7be, Вы писали:
0>Здравствуйте, nikov, Вы писали:
N>>А вдруг конструктор успел захватить какие-то unmanaged ресурсы до того как кинул исключение. Если не вызывать финализатор, то кто будет их освобождать? 0>Пускай он их и освобождает. Мне более правильно кажется логика конструирования/разрушения принятая в C++.
Дублирование получается.
Здравствуйте, Аноним, Вы писали:
А>А почему вам так кажется? Финализатор в любом случае принято писать устойчивым к повторном вызовам.
Речь совсем не о повторных вызовах.
Re[3]: Исключение в конструкторе + финализатор
От:
Аноним
Дата:
07.07.10 14:29
Оценка:
Здравствуйте, 0x7be, Вы писали:
0>Пускай он их и освобождает.
А если исключение бросает конструктор класса-наследника, то как родителю освободить ресурс?
Здравствуйте, samius, Вы писали:
0>>Пускай он их и освобождает. Мне более правильно кажется логика конструирования/разрушения принятая в C++. S>Дублирование получается.
Дублирование устраняется другими методами.
Здравствуйте, Аноним, Вы писали:
0>>Пускай он их и освобождает. А>А если исключение бросает конструктор класса-наследника, то как родителю освободить ресурс?
Финализаторы успешно сконструированных предков должны вызываться.
Здравствуйте, 0x7be, Вы писали:
0>Пускай он их и освобождает. Мне более правильно кажется логика конструирования/разрушения принятая в C++.
В C++ данные могут быть непроинициализированными. Почему деструктор никак не сможет установить, что конкретно было проинициализировано, а где просто мусор. Так что это вынужденная мера.
0>Ошибки в процессе конструирования должны обрабатываться самим конструктором.
Здравствуйте, 0x7be, Вы писали:
0>Финализаторы успешно сконструированных предков должны вызываться.
Здесь, есть еще такой ньюанс — в managed языках создание объекта идет, не как в C++ от предка к родителю, а фактически наоборот (как в java):
class X : Base { public X(int x) { super(x); } }
B Eсли например, вызвать в конструкторе предка виртуальный метод переопределенный в потомке, то вызовется метод потомка. Т.е. с точки зрения среды гораздо сложнее сообразить, кто уже сконструирован, а кто нет.
Здравствуйте, Mystic, Вы писали:
M>В C++ данные могут быть непроинициализированными. Почему деструктор никак не сможет установить, что конкретно было проинициализировано, а где просто мусор. Так что это вынужденная мера.
С выделенным не согласен. Это очень логичное поведение, целиком укладывающееся в идею, что язык предоставляет программисту ряд инвариантов.
0>>Ошибки в процессе конструирования должны обрабатываться самим конструктором. M>Это неудобно, потому что усложняет конструктор.
Количество сложности не изменяется. Усложняется конструктор, упрощается финализатор.
Здравствуйте, Аноним, Вы писали:
А>B Eсли например, вызвать в конструкторе предка виртуальный метод переопределенный в потомке, то вызовется метод потомка.
А это второй большой глюк, за который авторам С# надо что-нибудь оторвать
Здравствуйте, 0x7be, Вы писали:
0>Здравствуйте, Аноним, Вы писали:
А>>А почему вам так кажется? Финализатор в любом случае принято писать устойчивым к повторном вызовам. 0>Речь совсем не о повторных вызовах.
Написание такого финализатора очень похоже на реализацию финализатора для неполностью сконструированного объекта.
Короче — подсказываю — вам так кажется, потому что вы к этому привыкли. А на самом деле — в c++ тут допущена ошибка дизайна, которую подправили в .net.
Re[7]: Исключение в конструкторе + финализатор
От:
Аноним
Дата:
07.07.10 14:43
Оценка:
Здравствуйте, 0x7be, Вы писали: 0>А это второй большой глюк, за который авторам С# надо что-нибудь оторвать
Это не баг, а фича Причем не C#, а всего .net, и как я уже говорил, специфичная не только для .net.
Видимо, эта "фича" что-то упрощает для среды исполнения управляемых языков, самому если честно всегда было интересно — что конкретно.
Здравствуйте, Аноним, Вы писали:
А>Короче — подсказываю — вам так кажется, потому что вы к этому привыкли. А на самом деле — в c++ тут допущена ошибка дизайна, которую подправили в .net.
Не согласен. Я считаю, что С++-ный подход вернее. Мои аргументы заключается в том, что тамошняя система в бОльшей степени сохраняет гарантии программисту относительно целостности объекта при вызове его методов (в т.ч. и деструктора). Это, кстати, относится и к возможности вызывать виртуальные методы в конструкторе.
Почему Вы считаете, что это неверный подход?
Здравствуйте, Аноним, Вы писали:
0>>А это второй большой глюк, за который авторам С# надо что-нибудь оторвать А>Это не баг, а фича Причем не C#, а всего .net, и как я уже говорил, специфичная не только для .net. А>Видимо, эта "фича" что-то упрощает для среды исполнения управляемых языков, самому если честно всегда было интересно — что конкретно.
Не только для .net. В Дельфи, например, тоже же самое творится. Но там еще к тому же можно:
1. Создавать экземпляры абстрактных классов.
2. Не вызывать конструктор/деструктор базового класса.
3. Подёргать методы базового класса ДО вызова его конструктора или ПОСЛЕ вызова его деструктора
Только за такие "фитчи" дизайнерам языка надо что-то очень плохое сделать
Здравствуйте, 0x7be, Вы писали:
0>Коллеги!
0>Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.
Откровенно говоря, конструктор для CLR уже отработал — память выделена, поля имеют дефолтные значения: null для ссылок, и нули для структур. Конструкторы, с которыми мы имеем дело в языках всего лишь производят инициализацию полей экземпляра, т.е являются всего лишь автоматически вызваемым методом вертающим void (как вы верно заметили в Delphi похожее поведение). Посему вызов финализатора — вполе оправданное действие, ведь код финализатора (грубо говоря Dispose) обязан работать как для неполностью созданного объекта, так и для уже "разрушенного" объекта.
Здравствуйте, 0x7be, Вы писали:
0>Коллеги! 0>Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.
Просто другая организация работы с памятью. Отсюда и другие правила поведения. В С++ правильно одно в силу стандарта С++ и ничего более. В дотнете и шарпе правильно своё в силу местных стандартов.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, 0x7be, Вы писали:
0>Здравствуйте, Mystic, Вы писали:
M>>В C++ данные могут быть непроинициализированными. Почему деструктор никак не сможет установить, что конкретно было проинициализировано, а где просто мусор. Так что это вынужденная мера. 0>С выделенным не согласен. Это очень логичное поведение, целиком укладывающееся в идею, что язык предоставляет программисту ряд инвариантов.
Ну смотри. Такой код смотрится красиво, но с ним будут проблемы:
class test_t
{
private:
inner_test_t* test1;
inner_test_t* test2;
public:
test_t()
{
// Код, который может привести к исключению
test1 = new inner_test_t();
// Код, который может привести к исключению
test2 = new inner_test_t();
// Код, который может привести к исключению
}
virtual ~test_t()
{
if (test1) delete test1;
if (test2) delete test2;
}
};
Поэтому приходится либо чрезмерно усложнять конструктор, либо признать, что исключения в конструкторе штука стремная, и ее лучше инициализацию вынести в отдельный метод. Собственно говоря, ради возможности такой удобной записи все и затеяно начиная с Delphi.