
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.21] |
![]() |
|
Сообщ.
#1
,
|
|
|
собственно вопрос в том, используете ли Вы try-catch в конструкторе?
я считаю это неправильным. в конструкторе никогда не пишу код, который может сгенерировать исключение. |
![]() |
Сообщ.
#2
,
|
|
Конструктор сам может генерировать исключение. Объект тогда не создается и память освобождается автоматически. Деструктор не должен кидать исключение. Против try\catch в конструкторе\деструкторе ничего не имею против.
|
Сообщ.
#3
,
|
|
|
Цитата niXman @ собственно вопрос в том, используете ли Вы try-catch в конструкторе? Редко. Обычно лучше пропустить исключение дальше. Цитата я считаю это неправильным. в конструкторе никогда не пишу код, который может сгенерировать исключение. А почему? |
![]() |
Сообщ.
#4
,
|
|
Цитата в конструкторе никогда не пишу код, который может сгенерировать исключение. Это нормальная практика, когда конструктор кидает исключение. Вместе с оператором new, который кидает исключение, это вполне оправдано |
Сообщ.
#5
,
|
|
|
Цитата D_KEY @ А почему? потому что, для подобных ситуаций, я считаю, правильней добавить метод init()(или как-то так) и уже его обернуть в try-catch |
Сообщ.
#6
,
|
|
|
Цитата niXman @ Цитата D_KEY @ А почему? потому что, для подобных ситуаций, я считаю, правильней добавить метод init()(или как-то так) и уже его обернуть в try-catch Так почему так правильней? Добавлено И я не понимаю почему ты говоришь о блоке try catch в конструкторе вместе с обсуждением выброса исключений из конструктора, хотя это взаимоисключающие, чаще всего, вещи. |
Сообщ.
#7
,
|
|
|
D_KEY
Считается что можно поиметь утечку ресурсов Например ![]() ![]() class CA { hfile * file; ... CA(char * filename) { file = OpenFile(filename); ... if(...) throw errcode; ... } } файл останется открытым. Варианты решения: вынести открытие файла из конструктора в Init; вынести код с throw в Init; оптимальный вариант - завернуть hfile в класс с закрытием файла в деструкторе( Если ООП так уж ООП во всём) niXman Считаю правильнее - это не аргумент, у каждого решения должно быть обоснование Какие риски при использовании try-catch в конструкторе? Я никаких рисков не вижу, вполне можно использовать. |
Сообщ.
#8
,
|
|
|
Цитата vnf @ вынести открытие файла из конструктора в Init; вынести код с throw в Init; Есть мнение, что это костыли, а не полноценные варианты. Цитата vnf @ оптимальный вариант - завернуть hfile в класс с закрытием файла в деструкторе( Если ООП так уж ООП во всём) Не столько ООП, сколько RAII. |
Сообщ.
#9
,
|
|
|
vnf, считаю, что ресурсов без владельцев быть вообше не должно. То есть всегда держаться RAII. И костыли в виде всяких init() будут не нужны.
|
![]() |
Сообщ.
#10
,
|
|
Цитата Считается что можно поиметь утечку ресурсов ... Вообще-то приведенный код может спровоцировать утечку ресурсов в любом месте, не только в конструкторе. Нужно контролировать и понимать что ты пишешь. И вариант решения - освобождать ресурс прежде чем бросить исключение. (в том числе использовать обертку, которая освобождает в деструкторе самостоятельно). |
Сообщ.
#11
,
|
|
|
поясню.
собственно спор возник из-за того, что коллега, в свойствах класса имеет вектор указателей которые инициализируются при помощи new. я ему сказал, чтоб в векторе хранил смарт-поинтеры, т.к. при инициализации этих самых объектов, есть большая вероятность что кто-то из них бросит исключение. что он сделал: в конструкторе, инициализацию этих самых указателей, обернул в try-catch, добавил метод clear(), который вызывает в catch и в деструкторе. это меня повергло в шок. собственно я, никогда в конструкторе не инициализирую то, что может бросить исключение. в худшем случае, инициализирую такие свойства в списке инициализаторов(если возможно). почему? - да хз... просто привычка. не могу вспомнить откуда... с какого-то форума. как найду ссылку на тему - скину. |
Сообщ.
#12
,
|
|
|
Цитата niXman @ собственно спор возник из-за того, что коллега, в свойствах класса имеет вектор указателей которые инициализируются при помощи new. я ему сказал, чтоб в векторе хранил смарт-поинтеры, т.к. при инициализации этих самых объектов, есть большая вероятность что кто-то из них бросит исключение. что он сделал: в конструкторе, инициализацию этих самых указателей, обернул в try-catch, добавил метод clear(), который вызывает в catch и в деструкторе. Тут ты прав. Цитата собственно я, никогда в конструкторе не инициализирую то, что может бросить исключение. в худшем случае, инициализирую такие свойства в списке инициализаторов(если возможно). почему? - да хз... просто привычка. не могу вспомнить откуда... с какого-то форума. как найду ссылку на тему - скину. А вот это уже непонятно и мало связано с описанным случаем. |
Сообщ.
#13
,
|
|
|
Цитата vnf @ Считается что можно поиметь утечку ресурсов Например ![]() ![]() class CA { hfile * file; ... CA(char * filename) { file = OpenFile(filename); ... if(...) throw errcode; ... } } файл останется открытым. При работе с неуправляемыми ресурсами try-catch можно использовать как раз с целью предотвращения утечек. ![]() ![]() class CA { public: CA(char const *filename) { file = OpenFile(filename); if (!file) throw FailedToOpenFile(filename); try { .... // код, потенциально генерирующий исключения } catch (...) { CloseFile(file); throw; } } .... private: hfile *file; }; Это, в общем-то, известная идиома. |
Сообщ.
#14
,
|
|
|
Цитата D_KEY @ А вот это уже непонятно и мало связано с описанным случаем. я никогда не допускаю в коде, ситуацию, двусмысленную/неопределенную. сейчас ссылку поищу... |
Сообщ.
#15
,
|
|
|
Цитата niXman @ Цитата D_KEY @ А вот это уже непонятно и мало связано с описанным случаем. я никогда не допускаю в коде, ситуацию, двусмысленную/неопределенную. сейчас ссылку поищу... А как с помощью исключений из конструктора можно допустить двусмысленную ситуацию? |
Сообщ.
#16
,
|
|
|
Цитата D_KEY @ Цитата niXman @ Цитата D_KEY @ А вот это уже непонятно и мало связано с описанным случаем. я никогда не допускаю в коде, ситуацию, двусмысленную/неопределенную. сейчас ссылку поищу... А как с помощью исключений из конструктора можно допустить двусмысленную ситуацию? так, что не понятно, в каком состоянии находятся свойства класса. а для того, чтоб не городить код, проверяющий/утилизировавший свойства класса, проще исключения вообще не генерировать из конструктора. мое имхо. |
Сообщ.
#17
,
|
|
|
Цитата niXman @ так, что не понятно, в каком состоянии находятся свойства класса. После исключения??? Ни в каком. Объект будет разрушен, а память освобождена. Цитата а для того, чтоб не городить код, проверяющий/утилизировавший свойства класса... Так и не надо его городить. Цитата проще исключения вообще не генерировать из конструктора. мое имхо. Мне кажется, что оно основано на непонимании конструкторов, деструкторов и механизма исключений. |
Сообщ.
#18
,
|
|
|
Цитата niXman @ никогда в конструкторе не инициализирую то, что может бросить исключение. в худшем случае, инициализирую такие свойства в списке инициализаторов Если в классе применяется RAII, что должно быть, то пофигу (если не учитывать эффективность), а если нет - то поаккуратнее с "инициализирую такие свойства в списке инициализаторов" ![]() ![]() class A { public: A() : ..., p(new int) {} // небезопасно, в отличие от //A() : ... { p = new int; } - "безопасно", пока не будет меняться тело конструктора так, что станет возможно исключение после строчки p = new int; ~A() { delete p; } ... int* p; .... } |
Сообщ.
#19
,
|
|
|
Цитата Hryak @ ![]() ![]() A() : ..., p(new int) {} // небезопасно Почему? По-моему, безопасно, если Цитата Hryak @ ![]() ![]() int* p; описано последним полем, инициализация которого может кинуть исключение, и тело конструктора не бросает исключений (как, собственно, у вас и есть). |
Сообщ.
#20
,
|
|
|
MyNameIsIgor
А при модификации такого класса через год ты будешь помнить обо всех этих "если"?) |
Сообщ.
#21
,
|
|
|
Цитата LuckLess @ А при модификации такого класса через год ты будешь помнить обо всех этих "если"?) Это уже другой вопрос. Если вы читали топик, то я только "за" применения RAII. Понятно, что смартпоинтер будет лучше голого указателя. |
Сообщ.
#22
,
|
|
|
Цитата MyNameIsIgor @ безопасно, если Цитата Hryak @ ![]() ![]() int* p; описано последним полем, инициализация которого может кинуть исключение Кто сказал, что последним? Я же не зря многоточия поставил в описании класса. Одно дело - когда пишешь тело конструктора и четко видишь, что в нем, и только это влияет на безопасность кода, и другое - когда еще нужно держать в голове последовательность определения полей в классе... P.S. Мой пост был исключительно о том, что не всегда инициализация мемберов в initialization list лучше присвоения в теле конструктора. |
Сообщ.
#23
,
|
|
|
в общем, понятно, никто не против try-catch в конструкторе.
хотя, не могу вспомнить, чтоб в реальных проектах не новичков, встречал такое. вопрос закрыт. Добавлено Цитата Hryak @ Мой пост был исключительно о том, что не всегда инициализация мемберов в initialization list лучше присвоения в теле конструктора. буду знать. |
Сообщ.
#24
,
|
|
|
Цитата niXman @ хотя, не могу вспомнить, чтоб в реальных проектах не новичков, встречал такое Обычно тяжеловесные операции лучше не в конструкторе делать, а в методе вроде Init(). Тогда получается двухфазное создание экземпляров: 1) инстанс аллоцирован, но при попытке использования должны кидаться исключения/осетры/что-там-по-правилам-проекта. 2) инстанс инициирован, можно пользовать. Плюс такого подхода может быть в том, что шаг 1 исполняется очень быстро. Эдакий lazy load. Также, если два разных класса используют сервисы друг друга (например, подпиской через абстрактный интерфейс), то возникает парадокс курицы и яйца - непонятно, кого делать первым. А так - инстанциируем в любом порядке, и в init-коде даем ссылки друг на друга. |
Сообщ.
#25
,
|
|
|
Цитата Mr.Delphist @ Обычно тяжеловесные операции лучше не в конструкторе делать, а в методе вроде Init(). Тогда получается двухфазное создание экземпляров: 1) инстанс аллоцирован, но при попытке использования должны кидаться исключения/осетры/что-там-по-правилам-проекта. 2) инстанс инициирован, можно пользовать. Зачем так делать? Это лишняя путаница в коде. Цитата Смысл?Плюс такого подхода может быть в том, что шаг 1 исполняется очень быстро. Эдакий lazy load. Цитата Скорее всего тут ошибка уровня выше, хотя может я ошибаюсь.Также, если два разных класса используют сервисы друг друга (например, подпиской через абстрактный интерфейс), то возникает парадокс курицы и яйца - непонятно, кого делать первым. А так - инстанциируем в любом порядке, и в init-коде даем ссылки друг на друга. А можно пример такой ситуации? |
Сообщ.
#26
,
|
|
|
Цитата D_KEY @ Зачем так делать? Это лишняя путаница в коде. Например, оптимизация с точки зрения performance. Особенно на legacy-коде, когда взять и переписать по уму - без вариантов, ибо релиз на носу. Или когда физических ресурсов доступно в разы меньше, чем логических оберток вокруг них (эдаких смарт-хэндлов) - тогда инициализация аттачит инстанс с физическим ресурсом, а деинициализация - детачит. Цитата D_KEY @ Скорее всего тут ошибка уровня выше, хотя может я ошибаюсь. А можно пример такой ситуации? Например, когда есть две коллекции разных сущностей, но каждая из них заинтересована в нотификации об изменениях в другой (дабы ре-валидировать себя заново, допустим). Тут в конструкторах создаем, а в init() - оформляем подписки. |
Сообщ.
#27
,
|
|
|
Цитата Mr.Delphist @ Цитата D_KEY @ Зачем так делать? Это лишняя путаница в коде. Например, оптимизация с точки зрения performance. Особенно на legacy-коде, когда взять и переписать по уму - без вариантов, ибо релиз на носу. Так как это помогает с точки зрения performance? Цитата Или когда физических ресурсов доступно в разы меньше, чем логических оберток вокруг них (эдаких смарт-хэндлов) - тогда инициализация аттачит инстанс с физическим ресурсом, а деинициализация - детачит. Как это связано с методами init()? То есть вы предлагаете, чтобы код, использующий такие объекты, задумывался о реализации и сам вызывал методы инициализации/деинициализации? Не лучше ли инкапсулировать это поведение так, чтобы внешний код работал с такими объектами, как с обычными? Цитата А может нужен просто метод для подписки?Цитата D_KEY @ Скорее всего тут ошибка уровня выше, хотя может я ошибаюсь. А можно пример такой ситуации? Например, когда есть две коллекции разных сущностей, но каждая из них заинтересована в нотификации об изменениях в другой (дабы ре-валидировать себя заново, допустим). Тут в конструкторах создаем, а в init() - оформляем подписки. Init - дурацкое название, ибо неясно, что за ним скрывается. |