На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> двухшаговая инициализация
    Добрый день.
    Постоянно сталкиваюсь со следующей проблемой: есть фабричный метод для создания объектов, в зависимости от некоторых условий, также есть некий набор входных данных, который нужно передать во вновь созданный объект.
    Ниже псевдокод, описывающий текущее решение
    ExpandedWrap disabled
      class IObject
      {
      public:
          virtual ~IObject() {}
       
          virtual void initialize( const ObjectData &objectData ) = 0;
      };
       
      class AbstractObject : public IObject
      {
      public:
          virtual void initialize( const ObjectData& objectData ) override
          {
              m_objectData = &objectData;
          }
       
      private:
          ObjectData *m_objectData;
      };
       
      class ObjectA : public AbstractObject {};
      class ObjectB : public AbstractObject {};
       
      IObject* create( int type )
      {
          if( type == 1 ) return new ObjectA;
          return newObjectB;
      }


    соответственно используется примерно так:
    ExpandedWrap disabled
      IObject *obj = create( type );
      obj->initialize( objectData );

    в целом, данный подход устраивает, если бы не одно НО: так как ObjectData передается не в конструктор, то внутри AbstractObject мне приходится держать не константную ссылку, как хотелось бы, а простой указатель. С одной стороны я знаю, что данный указатель получен из ссылки, и, таким образом, не может быть nullptr. Однако при разрастании кода, об этом можно забыть и все-таки влепить проверки из серии if( m_objectData ).

    Альтернативный подход, который я вижу - это передача ObjectData в конструкторе:
    ExpandedWrap disabled
      class IObject
      {
      public:
          virtual ~IObject() {}
      };
       
      class AbstractObject : public IObject
      {
      public:
          AbstractObject( const ObjectData &objectData ) {}
      };
       
      class ObjectA : public AbstractObject
      {
      public:
          ObjectA( const ObjectData &objectData ) : AbstractObject( objectData ) {}
      };
       
      class ObjectB : public AbstractObject
      {
      public:
          ObjectB( const ObjectData &objectData ) : AbstractObject( objectData ) {}
      };
       
      IObject* create( int type, const ObjectData &objectData )
      {
          if( type == 1 ) return new ObjectA( objectData );
       
          return new ObjectB( objectData );
      }


    Такой вариант меня не устраивает прежде всего тем, что в каждом наследуемом классе необходимо писать один и тот же код, для передачи ObjectData в базовый класс.

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

    Возможен ли "третий" вариант, в котором нет вышеперечисленных минусов?
      Вообще-то в понятии фабричного метода, как и понятии фабрики, обычно подразумевается, что он занимается инициализацией создаваемого экземпляра. Пока объект не создан, его нет, на его месте куча мусора. Не даром же у нас этим занимаются конструкторы – специальные методы класса, от которых зависит наполнение кучи мусора на месте атрибутов объекта его полноценными инвариантами. Так что создание подразумевает инициализацию в том числе. Выходит, что первый вариант априори несостоятелен, если рассматривать его с позиции корректности реализации парадигмы фабричного метода.
      Другое дело, что конструкторы не зря умеют перегружаться, т.к. архитектура класса может предусматривать для инициализации, вообще говоря, несколько разных способов задавать эти инварианты. Но это и привносит неудобство в том смысле, что фабрикам приходится как-то с этой неоднозначностью разбираться. ИМХО наиболее простой, хоть и далеко не изящный, вариант – это предусмотреть в фабричном методе все те действия, которые иначе делали бы перегруженные конструкторы, и имитировать перегрузку конструкторов перегрузкой фабричного метода и/или простыми if()/switch()-ами.
      Более изящный, но и посложнее, вариант. Методы классов, которые фабричному методу, возможно, понадобятся для инициализации, сделать приватными, и пусть он занимается инвариантами посредством них (вместо того, чтобы явно стучаться к полям объекта), и при этом сам фабричный метод должен быть другом создаваемых им классов. Идеально, если интерфейс инициализации может быть определён в базовом классе и для всех производных останется неизменным. Это позволит объявить фабричный метод другом только одному, базовому, классу и не заморачиваться приватными методами инициализации в производных.
        Twilight, посмотри такой вариант, может понравится:

        ExpandedWrap disabled
          #include <iostream>
           
          struct BaseData {
            virtual std::string getValue() = 0;
          };
           
          struct ObjectData1: public BaseData {
            std::string str;
            ObjectData1(std::string iStr):str(iStr){}
            std::string getValue() override {
              return str;
            };
          };
           
          struct ObjectData2: public BaseData {
            int Int;  
            ObjectData2(int iInt):Int(iInt){}
            std::string getValue() override {
              return std::to_string(Int);
            };
          };
           
          template <typename T>
          class Factory {
            public:
              static T* generate(BaseData *objectData) {
                T* Tmp = new T();
                Tmp->init(objectData);
                return Tmp;
              }
          };
           
          class Initializer: public Factory<Initializer> {
               BaseData *objectData;
            public:
              void init(BaseData *iObjectData) {
                objectData = iObjectData;
                std::cout << objectData->getValue() << ": " << typeid(*objectData).name() << "\n";
              }
          };
           
          int main() {
            auto *Data1 = new ObjectData1("String");  
            auto *Data2 = new ObjectData2(10);
            auto *I1 = Factory<Initializer>::generate(Data1);
            auto *I2 = Factory<Initializer>::generate(Data2);
            return 0;
          }
          Qraizer я понимаю, что в данном случае это не совсем "эталонный" фабричный метод, но его всегда можно переписать следующим образом
          ExpandedWrap disabled
            IObject* create( int type, const ObjectData &objectData )
            {
                IObjectData *result = nullptr;
             
                if( type == 1 ) result = new Object1;
                else result = new Object2;
             
                result->initialize( objectData );
             
                return result;
            }


          В реальном коде вызовов initialize вынесен из метода create() в силу архитектуры ПО и контракта с прикладной частью.

          Что касается перегрузки конструкторов или создание с помощью дружественных методов: в моем случае ObjectData это один тип, с помощью которого инициализируются все типы объектов. В 99% случаев там нет данных, которые относились бы к Object1 и не относились бы к Object2. Получается, что разделение ObjectData на части CommonObjectData и Object1Data, Object2Data будет несколько искусственным. В дополнение, в один момент времени существуют только один экземпляр IObject ( по-факту - это работа с подключенным железом )


          JoeUser если честно я не совсем понял, твое предложение. Оно выглядит как усложненный вариант 1, но не решающее его проблему: хранение в классе указателя на BaseData
            В таком случае, Twilight, коли, в связи с особенностями архитектуры приложения, у тебя не полностью соблюдаются принципы надёжного кода, и ты перекладываешь ответственность за безгрешность с компилятора на себя, то на вот это:
            Цитата Twilight @
            Однако при разрастании кода, об этом можно забыть и все-таки влепить проверки из серии if( m_objectData ).
            можно ответить, что ты можешь в суматохе написать и
            ExpandedWrap disabled
              obj->initialize( *(ObjectData*)nullptr );
            так что проверки ничуть не лишни.
              Да, из-за особенности архитектуры я не могу вызывать initialize в точке создания объекта, однако я могу создавать объект в точке его инициализации.
              Но вопрос был совсем не про это.

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

              Я все-таки надеялся, что к существующим решениям
              1 - мало однотипного кода и однотипных описаний классов, но при этом сам следи за указателем
              2 - много однотипного кода, но никаких указателей
              можно добавить
              3 - мало однотипного кода и нет указателей
                Цитата Twilight @
                ... Однако при разрастании кода, об этом можно забыть и все-таки влепить проверки из серии if( m_objectData ).

                Это в данном случае будет бесполезно.
                Поскольку в классе "AbstractObject" указатель не обнуляется.
                ---
                По моему, ты не о том беспокоишься.
                Вот что получится, если в связи с ростом объёма
                будет забыта 2-ая строчка ? Вот так:
                ExpandedWrap disabled
                      IObject *obj = create( type );
                  //    obj->initialize( objectData );

                При этом не инициализированный указатель может иметь любое значение.
                ---
                Также, не удачно выглядит возврат void функцией initialize.
                Даже если на первый взгляд функция примитивна.
                Но естественно предположить, что однажды, перед присваиванием указателя
                захочется проведить правильность данных objectData.
                И тогда функция станет не столь примитивна. А указатель может остаться
                равным NULL (несмотря на то, что указатель должен быть получен из ссылки) - бессмысленно его использовать,
                если данные дефектны. Значение, равное NULL, может служить индикатором "что-то пошло не так".
                Сообщение отредактировано: ЫукпШ -
                  Цитата Twilight @
                  Если единственный вариант решения проблемы с хранением указателя при использовании метода initialize( ObjectData ) - это передача ObjectData через всю иерархию наследования ( что в моем случае выглядит не совсем красиво, так как в половине случаев, данный параметр просто передается ниже по цепочке наследования ), то вопрос я снимаю.
                  Я в свою очередь не понял источника проблемы. Почему нельзя создать прямо в нужной точке вместо инициализации, а нужно именно создать заранее и только потом инициализировать. Всё равно ж между ними объекта ещё нет, только кучка байтиков. И даже если надо именно так, то почему нельзя поместить это в саму фабрику, чтобы снаружи всё равно было одно действие, а не два. В конце концов когда бы делаем new SomeClass(bla_bla_bla), компилятор выполняет два действия, зато атомарно: распределение памяти и вызов конструктора; и то же с delete. И всё ради того, чтоб человек случайно не ошибался, и снаружи делал одно действие.
                  Ну да ладно, нельзя так нельзя. Но вот теперь я уже не понимаю, зачем передавать параметры инициализации вниз по цепочке иерархии, если можно просто ограничиться одним невиртуальным методом в базовом классе? Зовёшь, тот отрабатывает, и вуаля.

                  Добавлено
                  И собственно.
                  Цитата Twilight @
                  Однако при разрастании кода, об этом можно забыть и все-таки влепить проверки из серии if( m_objectData ).
                  Если тебя заботит такая мелочь, то и другие тоже должны бы, причём более не мелочи, но ты согласен с ними мириться. Почему бы и с этой мелочью не смириться?

                  Добавлено
                  В общем, я не понимаю истоков проблемы. Откуда такие странные ограничения на создание, почему не подходят общие паттерны проектирования итп. Потому посоветовать что-то дельное вряд ли выйдет. А вот гадание по кофейной гуще запросто, но как-то это не серьёзно.
                    Цитата ЫукпШ @
                    Это в данном случае будет бесполезно.
                    Поскольку в классе "AbstractObject" указатель не обнуляется.

                    Как я писал в первом посте - это не совсем реальный код, а некий псевдокод, который писался прямо здесь "от руки"


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

                    Потому-что там где ты смотришь их нет.
                    Давай остановимся на варианте - что все можно, потому-что вопрос совсем не про это.

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

                    Вопрос - можно ли избежать данных проблем?
                      Зачем тут вообще указатель или ссылка на ObjectData? Если хочешь избежать копирования, и ты можешь менять ObjectData, то напиши просто move конструктор для него и перемещай в класс.
                        Twilight, ты не понимаешь. Зачастую, гораздо чаще, чем ожидают спрашивающие, задают не тот вопрос: описывают не саму проблему, а следствия, и просят совета, как эти следствия побороть. Тогда как, описав саму проблему, получают хороший совет очень быстро. Обычно проблем, подобных твоей, не возникает, если воспользоваться описанными выше решениями. Они ведь не просто так были озвучены, они дают понять, что твой код столкнулся с проблемами, потому что спроектирован не вполне корректно. Но т.к. у тебя особая ситуация, и эти решения тебе по каким-то, неважно каким, объективным причинам не подходят, для адекватного ответа нам нужно знать больше конкретики. Мне, чтобы посоветовать по существу, нужно понимать что можно, что нельзя, а что и просто необходимо, а я, как выше писал, не могу для себя эти критерии сформулировать по той информации, что есть.
                        Ну попробую ещё раз ткнуть пальцем наобум. Если ты сделаешь базовый класс виртуальным, ты сможешь – и даже должен будешь – указывать его конструирование точно по месту создания объектов. И неважно, какое место в иерархии они занимают, конструирование виртуальных базовых классов указывается всегда в конкретном производном конструкторе и не передаются по цепочке. На, вот, пример:
                        ExpandedWrap disabled
                          #include <iostream>
                           
                          struct A
                          {
                            explicit A(int i=0)               { std::cout << "A::A(" << i << ')' << std::endl; }
                          };
                           
                          struct B: virtual A
                          {
                            explicit B(int i): A(i+1)         { std::cout << "B::B(" << i << ')' << std::endl; }
                          };
                           
                          struct C: virtual B
                          {
                            explicit C(int i): B(i+1)         { std::cout << "C::C(" << i << ')' << std::endl; }
                          };
                           
                          struct D: C
                          {
                            explicit D(int i): C(i+1), B(2*i) { std::cout << "D::D(" << i << ')' << std::endl; }
                          };
                           
                          int main()
                          {
                            D d(10);
                          }
                        ExpandedWrap disabled
                          A::A(0)
                          B::B(20)
                          C::C(11)
                          D::D(10)
                        Но что-то мне подсказывает, это не то, что тебе нужно.

                        Добавлено
                        Если совсем откровенно, я даже не пойму самого вопроса. С одной стороны ты не можешь инициализировать объект одновременно с его созданием, но с другой почему-то можешь, но тебе это неудобно. :huh: Так можешь или таки не можешь? Может быть "хочешь или не хочешь" спросить было правильнее? Следи за мысля́ми. Если можешь, то фабрика с инициализатором внутри вполне отличное решение. Если не можешь, то конструкторы по-любому не помогут никак. А если помогут, и дело лишь в дублировании кода передачи параметра, то это мелочь по сравнению с проверкой поинтера на валидность. Короче, я запутанный.
                          Qraizer, чел (ИМХО) просто загоняется! Все же просто как грабли:

                          1) Хочешь "виртуальную" (отложенную) инициализацию - используй фабрики
                          2) Хочешь изменение инициализации в рантайме - используй наследование и виртуальные функции
                          3) Хочешь изменение инициализации в компиле-тайме - используй CRTP

                          А вот использование ИСКЛЮЧИТЕЛЬНО ссылок щястья не гарантирует ... т.к. такая ссылка может алиасить "нулевой" или "мусорный" указатель. Ну или, как говорится, работай без хипа и обрети бессмертие! :lol:
                            Не обязательно, JoeUser. Ситуации всякие бывают, иногда действительно хочется странного, и не по капризу. C++ много что не запрещает или наоборот требует абсолютно, практически любой аспект можно обойти, было бы желание. Но чем опаснее хотелка, тем сложнее это сделать случайно, т.к. необходимые конструкции нужно писать руками и намеренно.
                            Мне вот как-то надо было, к примеру, изменить порядок вызова конструкторов в классе. Если б я не сообразил, как, то пришлось бы идти на форум, и если б я спросил "как мне изменить порядок инициализации подобъектов", был бы наверняка послан к индусам. Так что я бы спрашивал не это, я бы описал постановку задачи, из которой путём рассуждений следовала бы необходимость смены порядка, и уже как итог спросил бы совета. Нет, я не пришёл, потому что сообразил, как, и сделал. Цель была достигнута. Но не исключено, что спроси я на форуме совета, мне бы предложили куда лучшее решение, не требующее странного, которое однако ускользнуло от моего замыленного взгляда.

                            Добавлено
                            Twilight, считай это развёрнутым объяснением, почему твоя тема кажется... э-э-э, странной.
                            Сообщение отредактировано: Qraizer -
                              Цитата Qraizer @
                              Не обязательно, JoeUser.

                              Согласен - не вопрос! А вопрос в другом, в "деревенской мудрости" (типа) - "семь раз отмерь, один раз отрежь". Это я про предварительный анализ и проектирование архитектуры приложения/либы. Да, порой кажется, вот он САМ Я вступил в неравную борьбу с энтропией, с которой еще никто не сталкивался ... А потом на утро, после жирной яичницы с беконом и стаканом рассола приходит понимание ... тут 98% классических решений вписывается, нужно только 2% предусловий определить и оформить правильно.
                                Цитата Twilight @
                                .. я знаю, что данный указатель получен из ссылки, и, таким образом, не может быть nullptr. Однако при разрастании кода, об этом можно забыть и все-таки влепить проверки из серии if( m_objectData ).

                                Оберни указатель m_objectData классом, сделай свой "smart pointer".
                                В процедуре замены возвращаемого значения типа "bool" вставь
                                диагностику. Или генерируй исключение, по вкусу.
                                Тогда любая попытка сделать "if( m_objectData )" приведёт к сообщению об ошибке.
                                Сообщение отредактировано: ЫукпШ -
                                  ЫукпШ, не не не ... тут еще есть одна сторона вопроса. Вот ты спроси Килю "что ты больше всего любишь в своей жизни?". Он тебе ответит - RAII. Кстати - смарт-поинтеры из того же балета "Леблядиное Озеро". Да идея - супер, да удобно использование. Но не нужно расслабляться, ибо есть еще одна неприятная сторона жысти и программирования. Есть "нестабильные" ресурсы. Объясняю на пальцах...

                                  1) Запилили в RAII обертку указатель на выделенную память - супер, и нет проблем
                                  2) Запилили в RAII обертку хендл на файл, вроде все пучком. Одна незадача - файл оказался сетевым, и дворник обоссал маршрутизатор

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

                                  Вывод: как RAII, так и смарт-поинтерами нужно пользоваться с оглядкой на ситуацию и прогнозы. Они замечательны - но не панацея от всех бед!
                                    Цитата JoeUser @
                                    Вывод: как RAII, так и смарт-поинтерами нужно пользоваться с оглядкой на ситуацию и прогнозы. Они замечательны - но не панацея от всех бед!

                                    О чём это ?
                                    я не предлагаю следить за выделением/освобождением ресурса.
                                    Пишем свой смарт-поинтер. Задача которого следить
                                    за операцией "if(ptr)".
                                    Например:
                                    Скрытый текст

                                    ExpandedWrap disabled
                                      // --------------------------------------------------------------------------
                                      // FILE SPTR.h 2020.08.05
                                      // --------------------------------------------------------------------------
                                      // класс - указатель
                                      // --------------------------------------------------------------------------
                                      #ifndef __SPTR_20200805_H
                                      #define __SPTR_20200805_H
                                      // --------------------------------------------------------------------------
                                      template<class T>
                                      class SPTR
                                      {
                                       public:
                                       
                                       private:
                                       
                                       protected:
                                                   T    *ptr;
                                       
                                       public:
                                                   WINAPI SPTR(void);
                                                   WINAPI ~SPTR(void);
                                       
                                              T*   WINAPI operator ->(void) const;
                                            void   WINAPI operator = (T *ptr_);
                                              T&   WINAPI operator * (void) const;
                                      // операции преобразования типа
                                                   WINAPI operator T * (void) const;
                                       
                                                   WINAPI operator bool ();
                                      };
                                      // --------------------------------------------------------------------------
                                      template <class T>
                                      WINAPI SPTR<T>::SPTR(void) : ptr (NULL)
                                      {
                                      }
                                      // --------------------------------------------------------------------------
                                      template<class T>
                                      WINAPI SPTR<T>::~SPTR(void)
                                      {
                                      }
                                      // --------------------------------------------------------------------------
                                      template <class T> WINAPI SPTR<T>::operator bool ()
                                      {
                                       ::OutputDebugString(_T(__FUNCTION__));
                                      //..
                                      // вот тут можно возбудить исключительную ситуацию
                                      //..
                                       if(ptr)  return true;
                                       else     return false;
                                      }
                                      // --------------------------------------------------------------------------
                                      template <class T>
                                      void WINAPI SPTR<T>::operator = (T *ptr_)
                                      {
                                       ptr = ptr_;
                                      }
                                      // --------------------------------------------------------------------------
                                      template<class T>T& WINAPI SPTR<T>::operator *(void)const
                                      {
                                       return *ptr;
                                      }
                                      // --------------------------------------------------------------------------
                                      template<class T>T* WINAPI SPTR<T>::operator->(void)const
                                      {
                                       return ptr;
                                      }
                                      // --------------------------------------------------------------------------
                                      template<class T> WINAPI SPTR<T>::operator T* (void)const    
                                      {
                                       return ptr;
                                      }  
                                      // --------------------------------------------------------------------------
                                      #endif

                                    Сообщение отредактировано: ЫукпШ -
                                      Цитата JoeUser @
                                      Вот второй вариант, когда ресурсы могут менять свое состояние по внешним факторам - тут засада.
                                      Ресурс, который не твой, не твой ресурс, не так ли? Почему я пишу очевиднейшие вещи не в Холиварах?..
                                        Цитата Qraizer @
                                        Ресурс, который не твой, не твой ресурс, не так ли?

                                        Ну да ... а как быть с такими вот "неотдаваемыми" ресурсами? Твой вариант?
                                          Тебе нужен ресурс, но у тебя нет возможности им владеть – это значит, что у тебя нет ресурса, поэтому любая операция с ним потенциально может провалиться. В общем случае вариантов, кроме как аварийно завершить работу, не существует. За примером, когда иначе просто никак, далеко ходить не надо: когда ты ждёшь мьютекс на WaitForSingleObject(), например, а тебе в ответ WAIT_ABANDONED. С одной стороны мьютекс свободен, с другой – он освободился не в результате окончания транзакции по переходу охраняемой сущности в другое состояние, а значит охраняемая им сущность неинвариантна. Причём ты ещё и не знаешь, что именно в ней поломано. Привет всем апологетам TerminateThread(), которые т.о. запросто дропнут нитку посреди HeapAlloc(), например, и что теперь делать остальной части приложения?
                                          В частных случаях возможны варианты. В твоём примере есть возможность попробовать восстановить контроль над ресурсом. Но вот стоит ли? Обоссанный роутер будет подниматься из небытия куда дольше, чем юзер согласится ждать, наблюдая пред свои ясны очи "реконнектинг, приз вэйт" в твоём приложении. Если решишь, что стоит, ну тогда надо реконнектиться, конечно. Ну... раза три... или пять... Как думаешь, хватит или таки мало?
                                          Можно и других примеров привести. Типа исчерпанного места на диске, где просто попросить юзера освободить местечко, и дать добро на повтор... или лучше указать другое место, куда попробовать записать неписанное... или может быть лучше заранее предусмотреть себе место и зарезервировать, чтоб такой ситуации просто не должно было никогда происходить, как думаешь? А иначе просто ругаться, мол, дай места, а то работать не буду и не запускаться вовсе.
                                          Теория говорит, что если ты не знаешь хорошего решения возможной проблемы по умолчанию, не надо и пытаться ничего реализовывать, кинь экспешн, и пусть его ловит, обрабатывает и предпринимает действия по устранению тот слой кода, который знает. Вопрос лишь в грамотном проекте архитектуры твоего ПО, чтоб каждый слой был ответственен за свои задачи и звал интерфейсы других слоёв для их решения. Если спроектируешь неудобно, никакие паттерны не сделают приятно, всё равно будет винегрет из зависимостей, а вместо дерева интерфейсов ненаправленный граф. А в удобной архитектуре внезапно оказывается, что RAII вполне в неё вливается.
                                          Практика пробовала юзать т.н. возобновляемые отказы, когда вместо размотки стека можно предпринять действия по устранению причины отказа и просто продолжить, как ни в чём ни бывало. В SEH это вообще испокон веку было возможно. Я иногда пользовался, было интересно пощупать, но это всё было на синтетике. А на реальной практике ни разу. Вот что по этому пишет товарищ Страуструп в своём труде "Дизайн и эволюция языка C++"
                                          Цитата 16.6. Возобновление или завершение?
                                          При проектировании механизма обработки исключений наиболее спорным оказался вопрос, следует ли поддержать семантику возобновления или прекращения исполнения. Иными словами, может ли обработчик исключения потребовать возобновления исполнения с того момента, где было возбуждено исключение?
                                          ...
                                          Поначалу я думал так: «Почему бы и нет? Ведь сразу видно несколько ситуаций, в которых я мог бы воспользоваться возобновлением». Но в последующие четыре года моя точка зрения некоторым образом изменилась, и потому механизм обработки исключений в языке C++ поддерживает так называемую модель с завершением.
                                          Главные споры по поводу возобновления и завершения разгорелись в комитете ANSI C++. Вопрос обсуждался и на заседаниях всего комитета, и в рабочей группе по расширениям, и на вечерних технических обсуждениях, и в списках для рассылки по электронной почте. Споры продолжались с декабря 1989 г., когда был сформирован комитет ANSI C++, до ноября 1990 г. Тема вызвала большой интерес и у всего сообщества пользователей C++.
                                          ...
                                          Затем, на заседании в Пало Альто в ноябре 1991 г. мы услышали блестящий доклад Джима Митчелла (Jim Mitchell), работавшего в компании Sun, а перед этим – в Xerox PARC. Доклад содержал доводы в пользу семантики завершения, подкрепленные личным опытом и фактическими данными. На протяжении 20 лет Митчелл пользовался обработкой исключений в разных языках и на первых порах отстаивал семантику возобновления в качестве одного из главных проектировщиков и разработчиков системы Cedar/Mesa от компании Xerox. Его вывод звучал так:
                                          Цитата
                                          «Завершение следует предпочесть возобновлению. Это вопрос не вкуса, а многолетней практики. Возобновление выглядит соблазнительно, но это порочный метод».

                                          Свое утверждение Митчелл подкрепил рассказом о работе над несколькими операционными системами. Самым главным был пример системы Cedar/Mesa, которую написали программисты, любившие и умевшие пользоваться семантикой возобновления. Однако через десять лет в системе из полумиллиона строк остался лишь один случай использования возобновления – в запросе контекста. Поскольку и в данной ситуации оно фактически было не нужно, механизм возобновления исключили полностью, после чего скорость работы этой части системы значительно возросла. Во всех тех случаях, когда применялась операция возобновления (а это более десяти лет эксплуатации), появлялись определенные проблемы и приходилось искать более подходящий механизм. По сути дела, все применения возобновления были связаны с неумением отделить друг от друга различные уровни абстракции.
                                          Советую почитать 16.6 целиком, там много текста, с тезисами, историческими фактами, анализом и выводами.

                                          Если очень хочется попробовать попользовать возобновление при отказах, никто тебе не мешает передавать функтор в нижние слои кода. Попробуй. Может быть твои случаи тоже окажутся такими, каковые я тут называю странными.
                                          Сообщение отредактировано: Qraizer -
                                            Ну ок, сенкс, идею понял.

                                            Цитата Qraizer @
                                            Вот что по этому пишет товарищ Страуструп в своём труде "Дизайн и эволюция языка C++"

                                            Да, надо будет почитать. Тут, в принципе, "внеязыковой" вопрос.
                                              Цитата Qraizer @
                                              Привет всем апологетам TerminateThread(), которые т.о. запросто дропнут нитку посреди HeapAlloc(), например, и что теперь делать остальной части приложения?

                                              Такие существуют? :unsure:
                                              0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                              0 пользователей:


                                              Рейтинг@Mail.ru
                                              [ Script execution time: 0,0655 ]   [ 17 queries used ]   [ Generated: 19.04.24, 21:09 GMT ]