На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела:
1. Название темы - краткое описание кто/что против кого/чего
2. В первом сообщении - список параметров, по которым идет сравнение.
3. Старайтесь аргументировать свои высказывания. Фразы типа "Венда/Слюникс - ацтой" считаются флудом.
4. Давайте жить дружно и не доводить обсуждение до маразма и личных оскорблений.
Модераторы: Модераторы, Комодераторы
Страницы: (495) « Первая ... 478 479 [480] 481 482 ...  494 495  ( Перейти к последнему сообщению )  
> Delphi vs C++ vs C# , ну и Java немножко, где-то ближе к старшему байту номеров страниц
    Цитата D_KEY @
    Как видишь, я вообще деструктор не пишу

    Что интересно, деструктор не требуется даже в таком варианте:
    ExpandedWrap disabled
      #include <iostream>
      #include <memory>
       
      class InnerClass
      {
      public:
         InnerClass() {std::cout << "InnerClass::InnerClass" << std::endl;}
         ~InnerClass() {std::cout << "InnerClass::~InnerClass" << std::endl;}
      };
       
      class OuterClass
      {
      public:
         OuterClass()
         {
            m_inner.reset(new InnerClass());
            throw 10;
         }
        
      private:
         std::unique_ptr<InnerClass> m_inner;
      };
       
      int main()
      {
         try
         {
            OuterClass cls;
         }
         catch (...)
         {
            std::cout << "Exception thrown" << std::endl;
         }
      }
      Цитата jack128 @
      жесть.

      Не, нормально на самом деле. Явного указания требуют только POD - сишечное наследие.
        Цитата MyNameIsIgor @
        Не, нормально на самом деле. Явного указания требуют только POD - сишечное наследие.

        В С++11 ситуацию облегчили standard layout-типами.
          Цитата trainer @
          У меня никакого алеса не было. Родного с Delphi/VCL был только редактор форм и реакция на события компонента. AfterConstruction/BeforeDestruction использовал только один раз, в качестве костыля уже не помню к чему.
          А, ну да. Забыл приписать "... или положить на одну из моделей и юзать только (по возможности) вторую."

          Добавлено
          Вот, буквально на днях читал очередную лекцию на работе. Примеры оттуда.
          ExpandedWrap disabled
            /* Небезопасный код */
             
            class A
            {
              int *x;
              int *y;
             
            public:
              A(int a, int b): x(new int(a)),   y(new int(b))   {}  /* утекает x, если второй new провалится */
              A(const A& z)  : x(new int(z.x)), y(new int(z.y)) {}  /* утекает x, если второй new провалится */
             
              A& operator=(const A& z)
              {
                if (this == &z) return *this;     /* наивная попытка обеспечить безопасность коду */
             
                delete x;                         /* в случае сбоев мы теряем */
                delete y;                         /* старое состояние объекта */
             
                x = new int(z.x);
                y = new int(z.y);                 /* утекает x, если второй new провалится */
             
                return *this;
              }
             ~A()
              {
                delete x;
                delete y;
              }
            };
          Это демонстрация говностайла. Теперь будем исправлять.
          ExpandedWrap disabled
            /* Строгая гарантия соблюдается. Лобовое решение, где класс по-прежнему владеет обоими
               указателями. Цель достигнута, но код сложен для поддержки и дальнейшего развития. */
              
            #include <new>
             
            class A
            {
              int *x;
              int *y;
             
            public:
              A(int a, int b): x(new int(a))        /* здесь пока нет изменений в окружении */
              {
                try
                {
                  y = new int(b);                   /* а здесь уже есть, поэтому нужно позаботиться об x */
                }
                catch(std::bad_alloc)
                {
                  delete x;
                  throw;            /* исключение НЕ обрабатывается в этом месте, поэтому передаём его дальше */
                }
              }
             
              A(const A& z)  : x(new int(z.x))      /* здесь пока нет изменений в окружении */
              {
                try
                {
                  y = new int(z.y);                 /* а здесь уже есть, поэтому нужно позаботиться об x */
                }
                catch(std::bad_alloc)
                {
                  delete x;
                  throw;            /* исключение НЕ обрабатывается в этом месте, поэтому передаём его дальше */
                }
              }
              
              A& operator=(const A& z)
              {                                     /* сначала полностью подготовим новое состояние */
                int *tmp1 = new int(z.x),           /* здесь пока нет изменений в окружении */
                    *tmp2 = 0;
             
                try
                {
                  tmp2 = new int(z.y);              /* а здесь уже есть, поэтому нужно позаботиться о tmp1 */
                }
                catch(std::bad_alloc)
                {
                  delete tmp1;
                  throw;            /* исключение НЕ обрабатывается в этом месте, поэтому передаём его дальше */
                }
                /* теперь, когда новое состояние полностью готово, применим его к объекту */
                /* все четыре операции отказобезопасны */
                delete x;
                delete y;
                x = tmp1;
                y = tmp2;
             
                return *this;
              }
             
             ~A() throw()
              {
                delete x;
                delete y;
              }
            };
          Это решение, представляемое многими как очевидное и чуть ли не единственно возможное. Ну-ну.
          ExpandedWrap disabled
            /* Строгая гарантия соблюдается. Задача управления двумя указателями декомпозирована до двух
               задач управления по одному указателю. Класс разбит на два, каждый класс владеет только одним
               указателем, и один из них сделан базовым для второго.
               Цель достигнута, причём меньшим количеством кода и очень изящно, без try/catch. */
             
            #include <new>
            #include <algorithm>
             
            /* Вспомогательный класс для одного из указателей */
            class A_base
            {
              int *x;
             
            /* класс не предназначен для использования отдельно от второго, нужды в public-интерфейсе нет */
            protected:
              A(int a)     : x(new int(a))   {}     /* строго безопасно */
              A(const A& z): x(new int(z.x)) {}     /* строго безопасно */
             
              A& operator=(const A& z)              /* строго безопасно */
              {
                int *tmp = new int(z.x);            /* сначала подготовим новое состояние, */
                                                    /* а теперь применим его               */
                delete x;                           /* безотказно */
                x = tmp;                            /* безотказно */
              }
             
              /* безотказно */
             ~A()                  throw() { delete x; }
              /* вспомогательный метод для обмена состояниями двух объектов класса A_base
                 безотказно, т.к. std::swap() безотказен для POD-типов, в частности указателей */
              void swap(A_base& z) throw() { std::swap(x, z.x); }  
            };
             
            /* Основной класс для второго указателя, первый обслуживается базовым классом.
               Деление на два класса тут является деталью реализации, поэтому наследование приватно. */
            class A: private A_base
            {
              int *y;
             
            public:
              A(int a, int b): A_base(a),   y(new int(b))   {}      /* строго безопасно; если new провалится, */
              A(const A& z)  : A_base(z.x), y(new int(z.y)) {}      /* базовый класс сам за себя ответит      */
             
              A& operator=(const A& z)                              /* строго безопасно */
              { /* сначала подготовим новое состояние */
                A_base tmp1(z.x);           /* объект базового класса, он освободит x в случае провала new    */
                int   *tmp2 = new int(z.y); /* новый указатель для y, в случае провала за х ответственен tmp1 */
                
                /* а теперь применим его              */
                tmp1.swap(*this);           /* теперь наш базовый x во временном объекте, а новый x у нас */
                std::swap(y, z.y);          /* аналогично наш указатель y                                 */
                delete tmp2;                /* удаляем старый y                                           */
              }                             /* а тут удаляется временный объект с теперь уже старым x     */
             
             ~A()             throw() { delete y; }                 /* безотказно */
              
              /* последующий контент не требуется для примера, но желателен для рабочего приложения */
              /* безотказно */
              void swap(A& z) throw() { z.A_base::swap(*this); std::swap(y, z.y); }
            };
             
            /* этот контент - тоже последующий */
            namespace std
            {
             
            /* Делегируем специализации std::swap() для нашего класса его методу.
               Для A_base специализация не требуется, ибо вне A он всё равно недоступен, а для A она не нужна */
            template<> void swap(A& x, A& y) { x.swap(y); }
             
            }
          Короче. Надёжнее. Легко обобщаемо. Ни одного try/catch. Это к слову о разделении ответственности и RAII.
            Цитата
            Во-первых, дефолтная инициализация нулями по любому лучше (по кр.мере предсказуеемее), чем случайный мусор.

            Я не знаю, измерял ли кто нибудь это на последних версиях компилятора. Наши измерения показывали для VS 2005Ж явная инициализация нулями для достаточно простых объектов (типа, многоугольник) давала проигрыш примерно 25% по сравнению с инициализацией мусором. Неожиданно, внезапно, и неприятно, когда тебе этих объектов нужно создать сотни тысяч.
            Сообщение отредактировано: Бобёр -
              Цитата D_KEY @
              Сначала зануляете поля, потом что-то меняете. У нас же сразу вызываются конструкторы полей(а те вызывают конструкторы своих полей и т.д.)...

              Если слово "сразу" - это намек на некую скорость из-за отсутствия якобы лишних действий - то это заблуждение, т.к. единый memset (на современных компах) выполняется намного быстрее, чем выборочная дефолтная или зеро-инициализация каждого мембера в отдельности. В дельфях тоже есть функция Initialize() для выборочной зеро-инициализации auto-managed типов, но на практике вместо нее чаще используется сплошное зануление всей структуры или массива FillChar'ом (аналогом memset) - так проще и быстрее.
              А если "дело принципа", то ничего криминального в предварительном обнулении структуры нет, т.к.сплошные нули - это просто частный случай того же мусора ;) И если приплюснутые компиляторы этого не делают, будучи ограничены рамками "бородатых" традиций, закрепленных стандартами, то это не означает, что такой подход не годится для других языков и моделей ООП ;)

              Цитата D_KEY @
              Как видишь, я вообще деструктор не пишу :-?

              Отвечу твоими же словами:
              Цитата D_KEY @
              Ага, создали проблему и подставили костылик. Не надо вызывать деструкторы для несозданных объектов и все будет хорошо

              Типа "забыли", что объект может содержать не только subobject-ы целиком, но и ссылки\указатели на динамически выделенные классы\структуры\массивы. Ах, ну вот вам тогда костылик - заворачиваем любой указатель в фантик-класс под названием auto\unique_ptr - и вуа-ля, теперь он становится обычным subobject и по стандарту должен быть уничтожен вызовом собственного деструктора (если успел создаться до выброса исключения). Очень мило :)

              =================
              И вообще, в дельфи и С++ модели ООП "концептуально" разные, по крайней мере в плане конструкции\деструкции. В дельфи вообще нет такого понятия как subobject-ы, соотв-но нет и никакой жестко предопределенной иерархии автовызова конструкторов и деструкторов, и соотв-но нет понятий "сначала создали это, потом это" или "это уже создано, а это еще нет". Все создание объекта отдается на откуп программисту как "творцу\создателю" иерархии классов: хочешь\нужно вызвать конструктор родительского класса в дочернем конструкторе - вызывай, можно сразу перед своими действиями, а можно и после или между, или можно вообще ничего не вызывать и проинициализровать все по своему "с чистого листа" (если есть возможность, ес-но). Аналогично и с деструкторами - если сам не организуешь цепочку вызовов, то никто за тебя это делать не будет, а в случае фолта в конструкторе автоматом вызывается лишь деструктор самого создаваемого класса, который сам "по косвенным признакам" должен определить, что уже создано и должно быть удалено, а что нет. "Худо-бедно", но предварительное обнуление всех полей в InitInstance решает эту проблему на 99.(9)% (а "если где-то кое-где у нас порой" встречаются принципиально ненулевые инициализаторы типа INVALID_FILE_HANDLE, то к ним можно и дополнительный флаг-костылик приставить - "в семье не без урода" :)).
              Зато имеется возможность введения полиморфизма в сам процесс конструирования объекта, в том числе и за счет использования виртуальных методов. А это как-то погибче и ближе к природе, нежели только "тупо копировать" своих предков ;) Интересно, в С++ модели можно сделать "из обезьяны человека", или нужно непременно сначала целиком создать обезьяну и только затем "приводить ее в человеческий вид", повторяя весь эволюционный ход истории? :D В дельфийской модели в принципе можно, если "природа"\"творец" заложили в конструктор обезьяны возможность учета "виртуальных мутаций" непосредственно в процессе конструирования ;)
              PS: Разумеется все эти "зато" не могут служить аргументами в предвзятом споре в стиле "А из нашего окна площадь Красная видна, а из вашего окошка - только улица немножко!!!!"
                Цитата leo @
                Ах, ну вот вам тогда костылик ...
                Это костылик?? Побойся бога. Сырые указатели давно пора выпилить из плюсов вообще нафик. Только этого никогда не будет. Ты б ещё шаблоны костыликом к макросам обозвал.

                Добавлено
                По ходу leo решил по ... э-э-э, подскажите, какому разу всё мочалить сначала.
                  Цитата Бобёр @
                  явная инициализация нулями для достаточно простых объектов (типа, многоугольник) давала проигрыш примерно 25% по сравнению с инициализацией мусором

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

                  Цитата Qraizer @
                  Это костылик?? Побойся бога

                  Это обмен любезностями с теми, кто видит массу костылей в чужих огородах, но не замечает в своих ;)
                  Это костыль не сам по себе, а в сочетании с принципом не вызова деструктора "еще несозданного объекта" (по вашей терминологии), который просто вынуждает "порядочных" людей оборачивать указатели фантиками=костыликами (ну или городить огороды с try\catch, декомпозицией и т.п. как в твоих последних примерах)

                  Цитата Qraizer @
                  Сырые указатели давно пора выпилить из плюсов вообще нафик

                  D_KEY еще и деструкторами не пользуется - их тоже выкинуть?

                  Добавлено
                  Цитата Qraizer @
                  По ходу leo решил по ... э-э-э, подскажите, какому разу всё мочалить сначала

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

                    Ты о чем вообще? Слово "сразу" - это обозначение отсутствия лишней логики. Вопрос производительности пока лично я не поднимал.

                    Цитата
                    Цитата D_KEY @
                    Как видишь, я вообще деструктор не пишу :-?

                    Отвечу твоими же словами:
                    Цитата D_KEY @
                    Ага, создали проблему и подставили костылик. Не надо вызывать деструкторы для несозданных объектов и все будет хорошо


                    Эм. Какую проблему создали и какой костылик подставили?
                    Что в данном случае, на твой взгляд, является костылем? Избавление от рутинных и однообразных действий и выделение их в отдельную сущность? С каких пор это стало костылем?

                    Цитата
                    Ах, ну вот вам тогда костылик - заворачиваем любой указатель в фантик-класс под названием auto\unique_ptr - и вуа-ля, теперь он становится обычным subobject и по стандарту должен быть уничтожен вызовом собственного деструктора (если успел создаться до выброса исключения). Очень мило :)

                    Так в чем костыльность-то?
                    Т.е. писать освобождение руками - это не костыль, а автоматизировать это дело - костыль :-? И да, ведь поля - это просто частный случай, а не основной. И не ради него вводилось управления ресурсами. Наши файловые потоки закрывают файлы сами, а не ждут отдельной команды... И так у нас обстоит дело с любыми ресурсами.

                    Цитата
                    В дельфи вообще нет такого понятия как subobject-ы, соотв-но нет и никакой жестко предопределенной иерархии автовызова конструкторов и деструкторов, и соотв-но нет понятий "сначала создали это, потом это" или "это уже создано, а это еще нет". Все создание объекта отдается на откуп программисту как "творцу\создателю" иерархии классов: хочешь\нужно вызвать конструктор родительского класса в дочернем конструкторе - вызывай, можно сразу перед своими действиями, а можно и после или между, или можно вообще ничего не вызывать и проинициализровать все по своему "с чистого листа" (если есть возможность, ес-но).

                    В общем - каша. Ты даже не заметил, что "творец\создатель" у тебя в единственном числе... А реальные иерархии все-таки чаще создаются разными людьми.

                    Цитата
                    Зато имеется возможность введения полиморфизма в сам процесс конструирования объекта

                    Так это не нужно. Вернее ради этого не надо жертвовать разделениями обязанностей - отделяем "полиморфную" логику(работу с уже созданными объектами) от собственно конструирования объектов.

                    Цитата
                    в том числе и за счет использования виртуальных методов.

                    Ага, мы только предка конструируем, а уже дергаем методы недоделанного потомка. Супер просто :D

                    Цитата
                    PS: Разумеется все эти "зато" не могут служить аргументами в предвзятом споре в стиле "А из нашего окна площадь Красная видна, а из вашего окошка - только улица немножко!!!!"

                    Это и так всем понятно. Но холивар же ;)

                    Добавлено
                    Цитата leo @
                    Это костыль не сам по себе, а в сочетании с принципом не вызова деструктора "еще несозданного объекта" (по вашей терминологии), который просто вынуждает "порядочных" людей оборачивать указатели фантиками=костыликами

                    Эм... Обетки не для этого создавались. А для управления ресурсами. Данное применение - просто следствие, потому под определение костыля не попадает.

                    Цитата
                    D_KEY еще и деструкторами не пользуется - их тоже выкинуть?

                    Пользуюсь. Когда они нужны ;)
                      Цитата D_KEY @
                      А реальные иерархии все-таки чаще создаются разными людьми.


                      И что? Я тебе уже приводил аналогию (на которую ты мне ничего не ответил) что с таким же успехом можно придти к выводу что вызов виртуальных методов в базовом классе нужно запретить (не только из конструктора, а вообще). Ну, давай придем к такому выводу ;) И на том и разойдемся зафиксировав разногласия

                      Цитата D_KEY @
                      Ага, мы только предка конструируем, а уже дергаем методы недоделанного потомка. Супер просто


                      Тебе уже двое написали что нет такого понятия "конструируем предка". Не читатель?
                      :whistle:
                        Цитата Qraizer @
                        Короче. Надёжнее. Легко обобщаемо

                        э-э-э. Я правильно понял, что если нужно пять указателей хранить, то у меня пять уровней иерархии будет??
                          Цитата --Ins-- @
                          Я тебе уже приводил аналогию (на которую ты мне ничего не ответил) что с таким же успехом можно придти к выводу что вызов виртуальных методов в базовом классе нужно запретить (не только из конструктора, а вообще). Ну, давай придем к такому выводу ;) И на том и разойдемся зафиксировав разногласия

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


                            Нужно. Виртуальный метод используется в коде предка и он мог не ожидать того, как ты захочешь поступить с ним в потомке
                              Цитата --Ins-- @
                              Нужно. Виртуальный метод используется в коде предка и он мог не ожидать того, как ты захочешь поступить с ним в потомке

                              Пример, пожалуйста.
                                Цитата D_KEY @
                                Мы же обсуждали, забыл? Вы еще тогда путались в рекомендациях, когда вызывать конструктор базового класса, до инициализации полей или после


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

                                Добавлено
                                Цитата D_KEY @
                                Пример, пожалуйста.


                                Лень выдумывать. Есть код в базовом классе, который вызывает виртуальный метод. Ты его перекрываешь так, как проектировщик класса не рассчитывал. Разгребай. Ну, или лезь в код предка, но как я понял - тут программисту следует застрелиться
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (495) « Первая ... 478 479 [480] 481 482 ...  494 495


                                Рейтинг@Mail.ru
                                [ Script execution time: 0.6224 ]   [ 15 queries used ]   [ Generated: 28.07.26, 16:44 GMT ]