На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> try-catch в конструкторе? оО
    собственно вопрос в том, используете ли Вы try-catch в конструкторе?
    я считаю это неправильным. в конструкторе никогда не пишу код, который может сгенерировать исключение.
      Конструктор сам может генерировать исключение. Объект тогда не создается и память освобождается автоматически. Деструктор не должен кидать исключение. Против try\catch в конструкторе\деструкторе ничего не имею против.
        Цитата niXman @
        собственно вопрос в том, используете ли Вы try-catch в конструкторе?

        Редко. Обычно лучше пропустить исключение дальше.

        Цитата
        я считаю это неправильным. в конструкторе никогда не пишу код, который может сгенерировать исключение.

        А почему?
          Цитата
          в конструкторе никогда не пишу код, который может сгенерировать исключение.

          Это нормальная практика, когда конструктор кидает исключение. Вместе с оператором new, который кидает исключение, это вполне оправдано
            Цитата D_KEY @
            А почему?

            потому что, для подобных ситуаций, я считаю, правильней добавить метод init()(или как-то так) и уже его обернуть в try-catch
              Цитата niXman @
              Цитата D_KEY @
              А почему?

              потому что, для подобных ситуаций, я считаю, правильней добавить метод init()(или как-то так) и уже его обернуть в try-catch

              Так почему так правильней?

              Добавлено
              И я не понимаю почему ты говоришь о блоке try catch в конструкторе вместе с обсуждением выброса исключений из конструктора, хотя это взаимоисключающие, чаще всего, вещи.
                D_KEY
                Считается что можно поиметь утечку ресурсов

                Например
                ExpandedWrap disabled
                  class CA
                  {
                    hfile * file;
                    ...
                    CA(char * filename)
                   {
                       file = OpenFile(filename);
                       ...
                       if(...) throw errcode;
                       ...
                    }
                  }

                файл останется открытым.

                Варианты решения:
                вынести открытие файла из конструктора в Init;
                вынести код с throw в Init;
                оптимальный вариант - завернуть hfile в класс с закрытием файла в деструкторе( Если ООП так уж ООП во всём)

                niXman
                Считаю правильнее - это не аргумент, у каждого решения должно быть обоснование

                Какие риски при использовании try-catch в конструкторе?
                Я никаких рисков не вижу, вполне можно использовать.
                Сообщение отредактировано: vnf -
                  Цитата vnf @
                  вынести открытие файла из конструктора в Init;
                  вынести код с throw в Init;

                  Есть мнение, что это костыли, а не полноценные варианты.
                  Цитата vnf @
                  оптимальный вариант - завернуть hfile в класс с закрытием файла в деструкторе( Если ООП так уж ООП во всём)

                  Не столько ООП, сколько RAII.
                    vnf, считаю, что ресурсов без владельцев быть вообше не должно. То есть всегда держаться RAII. И костыли в виде всяких init() будут не нужны.
                      Цитата
                      Считается что можно поиметь утечку ресурсов
                      ...

                      Вообще-то приведенный код может спровоцировать утечку ресурсов в любом месте, не только в конструкторе. Нужно контролировать и понимать что ты пишешь.
                      И вариант решения - освобождать ресурс прежде чем бросить исключение. (в том числе использовать обертку, которая освобождает в деструкторе самостоятельно).
                        поясню.
                        собственно спор возник из-за того, что коллега, в свойствах класса имеет вектор указателей которые инициализируются при помощи new. я ему сказал, чтоб в векторе хранил смарт-поинтеры, т.к. при инициализации этих самых объектов, есть большая вероятность что кто-то из них бросит исключение.
                        что он сделал:
                        в конструкторе, инициализацию этих самых указателей, обернул в try-catch, добавил метод clear(), который вызывает в catch и в деструкторе. это меня повергло в шок.
                        собственно я, никогда в конструкторе не инициализирую то, что может бросить исключение. в худшем случае, инициализирую такие свойства в списке инициализаторов(если возможно).
                        почему? - да хз... просто привычка. не могу вспомнить откуда... с какого-то форума. как найду ссылку на тему - скину.
                          Цитата niXman @
                          собственно спор возник из-за того, что коллега, в свойствах класса имеет вектор указателей которые инициализируются при помощи new. я ему сказал, чтоб в векторе хранил смарт-поинтеры, т.к. при инициализации этих самых объектов, есть большая вероятность что кто-то из них бросит исключение.
                          что он сделал:
                          в конструкторе, инициализацию этих самых указателей, обернул в try-catch, добавил метод clear(), который вызывает в catch и в деструкторе.

                          Тут ты прав.

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

                          А вот это уже непонятно и мало связано с описанным случаем.
                            Цитата vnf @
                            Считается что можно поиметь утечку ресурсов

                            Например
                            ExpandedWrap disabled
                              class CA
                              {
                                hfile * file;
                                ...
                                CA(char * filename)
                               {
                                   file = OpenFile(filename);
                                   ...
                                   if(...) throw errcode;
                                   ...
                                }
                              }

                            файл останется открытым.

                            При работе с неуправляемыми ресурсами try-catch можно использовать как раз с целью предотвращения утечек.

                            ExpandedWrap disabled
                              class CA
                              {
                              public:
                                  CA(char const *filename)
                                  {
                                      file = OpenFile(filename);
                                      if (!file)
                                          throw FailedToOpenFile(filename);
                                      try
                                      {
                                          .... // код, потенциально генерирующий исключения
                                      }
                                      catch (...)
                                      {
                                          CloseFile(file);
                                          throw;
                                      }
                                  }
                                  ....
                              private:
                                  hfile *file;
                              };

                            Это, в общем-то, известная идиома.
                            Сообщение отредактировано: Masterkent -
                              Цитата D_KEY @
                              А вот это уже непонятно и мало связано с описанным случаем.

                              я никогда не допускаю в коде, ситуацию, двусмысленную/неопределенную. сейчас ссылку поищу...
                                Цитата niXman @
                                Цитата D_KEY @
                                А вот это уже непонятно и мало связано с описанным случаем.

                                я никогда не допускаю в коде, ситуацию, двусмысленную/неопределенную. сейчас ссылку поищу...

                                А как с помощью исключений из конструктора можно допустить двусмысленную ситуацию?
                                  Цитата D_KEY @
                                  Цитата niXman @
                                  Цитата D_KEY @
                                  А вот это уже непонятно и мало связано с описанным случаем.

                                  я никогда не допускаю в коде, ситуацию, двусмысленную/неопределенную. сейчас ссылку поищу...

                                  А как с помощью исключений из конструктора можно допустить двусмысленную ситуацию?

                                  так, что не понятно, в каком состоянии находятся свойства класса.
                                  а для того, чтоб не городить код, проверяющий/утилизировавший свойства класса, проще исключения вообще не генерировать из конструктора. мое имхо.
                                    Цитата niXman @
                                    так, что не понятно, в каком состоянии находятся свойства класса.

                                    После исключения??? Ни в каком. Объект будет разрушен, а память освобождена.

                                    Цитата
                                    а для того, чтоб не городить код, проверяющий/утилизировавший свойства класса...

                                    Так и не надо его городить.

                                    Цитата
                                    проще исключения вообще не генерировать из конструктора. мое имхо.

                                    Мне кажется, что оно основано на непонимании конструкторов, деструкторов и механизма исключений.
                                      Цитата niXman @
                                      никогда в конструкторе не инициализирую то, что может бросить исключение. в худшем случае, инициализирую такие свойства в списке инициализаторов

                                      Если в классе применяется RAII, что должно быть, то пофигу (если не учитывать эффективность), а если нет - то поаккуратнее с "инициализирую такие свойства в списке инициализаторов"
                                      ExpandedWrap disabled
                                        class A
                                        {
                                        public:
                                          A() : ..., p(new int) {} // небезопасно, в отличие от
                                          //A() : ... { p = new int; } - "безопасно", пока не будет меняться тело конструктора так, что станет возможно исключение после строчки p = new int;
                                          ~A() { delete p; }
                                        ...
                                          int* p;
                                        ....
                                        }
                                        Цитата Hryak @
                                        ExpandedWrap disabled
                                          A() : ..., p(new int) {} // небезопасно

                                        Почему? По-моему, безопасно, если
                                        Цитата Hryak @
                                        ExpandedWrap disabled
                                          int* p;

                                        описано последним полем, инициализация которого может кинуть исключение, и тело конструктора не бросает исключений (как, собственно, у вас и есть).
                                        Сообщение отредактировано: MyNameIsIgor -
                                          MyNameIsIgor
                                          А при модификации такого класса через год ты будешь помнить обо всех этих "если"?)
                                            Цитата LuckLess @
                                            А при модификации такого класса через год ты будешь помнить обо всех этих "если"?)

                                            Это уже другой вопрос. Если вы читали топик, то я только "за" применения RAII. Понятно, что смартпоинтер будет лучше голого указателя.
                                              Цитата MyNameIsIgor @
                                              безопасно, если
                                              Цитата Hryak @
                                              ExpandedWrap disabled
                                                int* p;

                                              описано последним полем, инициализация которого может кинуть исключение

                                              Кто сказал, что последним? Я же не зря многоточия поставил в описании класса.
                                              Одно дело - когда пишешь тело конструктора и четко видишь, что в нем, и только это влияет на безопасность кода, и другое - когда еще нужно держать в голове последовательность определения полей в классе...

                                              P.S. Мой пост был исключительно о том, что не всегда инициализация мемберов в initialization list лучше присвоения в теле конструктора.
                                                в общем, понятно, никто не против try-catch в конструкторе.
                                                хотя, не могу вспомнить, чтоб в реальных проектах не новичков, встречал такое.
                                                вопрос закрыт.

                                                Добавлено
                                                Цитата Hryak @
                                                Мой пост был исключительно о том, что не всегда инициализация мемберов в initialization list лучше присвоения в теле конструктора.

                                                буду знать.
                                                  Цитата niXman @
                                                  хотя, не могу вспомнить, чтоб в реальных проектах не новичков, встречал такое


                                                  Обычно тяжеловесные операции лучше не в конструкторе делать, а в методе вроде Init(). Тогда получается двухфазное создание экземпляров:
                                                  1) инстанс аллоцирован, но при попытке использования должны кидаться исключения/осетры/что-там-по-правилам-проекта.
                                                  2) инстанс инициирован, можно пользовать.

                                                  Плюс такого подхода может быть в том, что шаг 1 исполняется очень быстро. Эдакий lazy load.
                                                  Также, если два разных класса используют сервисы друг друга (например, подпиской через абстрактный интерфейс), то возникает парадокс курицы и яйца - непонятно, кого делать первым. А так - инстанциируем в любом порядке, и в init-коде даем ссылки друг на друга.
                                                    Цитата Mr.Delphist @
                                                    Обычно тяжеловесные операции лучше не в конструкторе делать, а в методе вроде Init(). Тогда получается двухфазное создание экземпляров:
                                                    1) инстанс аллоцирован, но при попытке использования должны кидаться исключения/осетры/что-там-по-правилам-проекта.
                                                    2) инстанс инициирован, можно пользовать.

                                                    Зачем так делать? Это лишняя путаница в коде.

                                                    Цитата
                                                    Плюс такого подхода может быть в том, что шаг 1 исполняется очень быстро. Эдакий lazy load.
                                                    Смысл?

                                                    Цитата
                                                    Также, если два разных класса используют сервисы друг друга (например, подпиской через абстрактный интерфейс), то возникает парадокс курицы и яйца - непонятно, кого делать первым. А так - инстанциируем в любом порядке, и в init-коде даем ссылки друг на друга.
                                                    Скорее всего тут ошибка уровня выше, хотя может я ошибаюсь.
                                                    А можно пример такой ситуации?
                                                      Цитата D_KEY @
                                                      Зачем так делать? Это лишняя путаница в коде.

                                                      Например, оптимизация с точки зрения performance. Особенно на legacy-коде, когда взять и переписать по уму - без вариантов, ибо релиз на носу.
                                                      Или когда физических ресурсов доступно в разы меньше, чем логических оберток вокруг них (эдаких смарт-хэндлов) - тогда инициализация аттачит инстанс с физическим ресурсом, а деинициализация - детачит.

                                                      Цитата D_KEY @
                                                      Скорее всего тут ошибка уровня выше, хотя может я ошибаюсь.
                                                      А можно пример такой ситуации?

                                                      Например, когда есть две коллекции разных сущностей, но каждая из них заинтересована в нотификации об изменениях в другой (дабы ре-валидировать себя заново, допустим). Тут в конструкторах создаем, а в init() - оформляем подписки.
                                                        Цитата Mr.Delphist @
                                                        Цитата D_KEY @
                                                        Зачем так делать? Это лишняя путаница в коде.

                                                        Например, оптимизация с точки зрения performance. Особенно на legacy-коде, когда взять и переписать по уму - без вариантов, ибо релиз на носу.

                                                        Так как это помогает с точки зрения performance?

                                                        Цитата
                                                        Или когда физических ресурсов доступно в разы меньше, чем логических оберток вокруг них (эдаких смарт-хэндлов) - тогда инициализация аттачит инстанс с физическим ресурсом, а деинициализация - детачит.

                                                        Как это связано с методами init()? То есть вы предлагаете, чтобы код, использующий такие объекты, задумывался о реализации и сам вызывал методы инициализации/деинициализации?
                                                        Не лучше ли инкапсулировать это поведение так, чтобы внешний код работал с такими объектами, как с обычными?

                                                        Цитата
                                                        Цитата D_KEY @
                                                        Скорее всего тут ошибка уровня выше, хотя может я ошибаюсь.
                                                        А можно пример такой ситуации?

                                                        Например, когда есть две коллекции разных сущностей, но каждая из них заинтересована в нотификации об изменениях в другой (дабы ре-валидировать себя заново, допустим). Тут в конструкторах создаем, а в init() - оформляем подписки.
                                                        А может нужен просто метод для подписки?
                                                        Init - дурацкое название, ибо неясно, что за ним скрывается.
                                                        0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                        0 пользователей:


                                                        Рейтинг@Mail.ru
                                                        [ Script execution time: 0,1304 ]   [ 16 queries used ]   [ Generated: 17.06.25, 04:24 GMT ]