На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> Вызов обобщенного метода
    Есть несколько классов, которые наследуются от CDialogEx.

    class CGreenDialog : public CDialogEx {
    public:
    void Calculate();
    }

    class CRedDialog : public CDialogEx {
    public:
    void Calculate();
    }


    В этих классах нужно добавить некий метод (например Calculate()), который потом будет должен вызываться обобщенно.

    Например:
    ExpandedWrap disabled
      CDialogEx* dlg[10];
      dlg[0] = new CGreenDialog();
      dlg[1] = new CRedDialog();
      //...
       
      for (i=0; i<10; i++)
         dlg[i]->Calculate(); //Так нельзя, т.к. в CDialogEx нет такого метода.


    Можно преобразовать так:
    ExpandedWrap disabled
      ((CRedDialog*)dlg[i])->Calculate();


    Но в dlg может быть класс другого типа.

    Подскажите пожалуйста, как это правильно реализовать?
      Простой вариант: добавляешь виртуальный метод Calculate() в CDialogEx и перекрываешь в каждом производном. Вызываешь через CDialogEx по ссылке или указателю, в run-time всё разрулится само.
      Есть более гибкий, но сложнее. Нужен?

      Добавлено
      Ладно, будем считать, что нужен.
      Создаёшь "интерфейс" в лице абстрактного класса, например, ICalculate. В нём объявляешь нужный тебе виртуальный метод Calculate().
      ExpandedWrap disabled
        struct ICalculate
        {
          virtual void Calculate() = 0;
        };
      Тем (и только тем) производным классам, которым этот интерфейс нужно реализовать, добавляешь его в базовые и перекрываешь метод Calculate(), как кому надо.
      ExpandedWrap disabled
        class CGreenDialog: public CDialogEx, public ICalculate
        {
        public:
          void Calculate();
        };
         
        class CRedDialog : public CDialogEx, public ICalculate
        {
        public:
          void Calculate();
        };
      Используешь Calculate() как часть ICalculate, а не CDialogEx, предварительно проверив, что используемый экземпляр на самом деле реализует ICalculate:
      ExpandedWrap disabled
        CDialogEx* dlg[10];
        dlg[0] = new CGreenDialog();
        dlg[1] = new CRedDialog();
        //...
         
        for (i=0; i<10; i++)
        {
          ICalculate* iptr = dynamic_cast<ICalculate*>(dlg[i]);
         
          if (iptr != NULL) iptr->Calculate();
        }
      Если знаком с COM, этот метод не должен шокировать.
      Сообщение отредактировано: Qraizer -
        Да, сложность заключалась именно в том, что нельзя вмешиваться в CDialogEx.

        Второй пример (дополнение) работает как надо! Большое спасибо!


        p.s. Так примерно и делал (с множественным наследованием), но использовал не struct, а class. И преобразование делал так (видимо неправильно):
        ExpandedWrap disabled
          //неправильный код
          ICalculate* iptr = (ICalculate*)(dlg[i]);
          if (iptr != NULL)
            iptr->Calculate();

        Поэтому программа вылетала с ошибкой.
          Цитата ViH @
          Так примерно и делал (с множественным наследованием), но использовал не struct, а class.
          Без разницы. Интерфейсы обычно содержат только публичные методы, т.к. описывают возможные воздействия на объект, реализующий этот интерфейс, поэтому struct просто позволяет экономить на неуказании public.
          Цитата ViH @
          И преобразование делал так (видимо неправильно):
          ...
          Поэтому программа вылетала с ошибкой.
          Да. Компилятор не видит способа преобразовать CDialogEx в ICalculate, т.к. между ними нет ни никакого родства, ни кастующих конструкторов или операторов. Поэтому применяет семантику reinterpret_cast<>, что неправильно. Тут нужна семантика static_cast, но для этого нужно как-то объяснить компилятору маршрут по иерархии, а значит через промежуточный общий узел. В твоём случае это один из производных классов, для которого и CDialogEx, и ICalculate являются базовыми, т.е. два каста: сначала в производный, потом в другой базовый. Но тут ты столкнёшься с той же самой проблемой: dlg может не быть ни одним из производных от CDialogEx. dynamic_cast<> имеет семантику как раз static_cast<>, но проверяет иерархию в run-time, поэтому идеально подходит. Он даже лучше, т.к. не требует промежуточных узлов. Опять же, если проводить аналогию с COM, то dynamic_cast<> — это аналог QueryInterface()

          Добавлено
          Да, и ещё пару советов на всякий случай.
          • Абстрактные классы — это не интерфейсы. Поэтому реализуя ими семантику интерфейсов, нужно быть осторожным. Лучше всего всегда наследовать их виртуально, тогда они гораздо сильнее будут похожи на интерфейсы. То же относится к расширениям интерфейсов: интерфейс, производный от другого интерфейса, хорошо имитируется абстрактным классом, виртуально унаследованным от базового абстрактного класса. И наоборот, базовые классы, даже если реализуют интерфейсы, но сами не являются ими, обычно не должны виртуально наследоваться. (Впрочем тут всё далеко не всегда однозначно. Интересно поговорить на эту тему можно, но лучше в другой теме, тут это оффтоп. У нас в Холиварах уже есть подобные.) Без крайней нужны вообще не стоит множественно наследовать реализации, лучше обойтись их агрегацией и организовать маршалинг к ним явными методами. Так код получается гораздо более управляемым.
          • Интерфейс обязательно должен реализовываться целиком. Нельзя допускать неполной его реализации с последующей дореализацией в другом производном классе. Хоть в контексте абстрактных классов язык это и позволяет, но такая практика противоречит принципам интерфейсно-ориентированного программирования. Кроме того, разбивая реализацию одного интерфейса по разным субъектам иерархии, можно быстро сделать код очень плохо управляемым. Всегда лучше разбить сам интерфейс на несколько попроще, что позволит реализовывать их независимо и по-отдельности.
            Спасибо большое за объяснение внутренней работы. Очень полезно!
              Цитата Qraizer @
              Если знаком с COM, этот метод не должен шокировать.

              Это из S.O.L.I.D, четвертый принцип - "Разделение интерфейсов" Ответил просто так, как-то топик попался на глаза и увидел знакомое :)
              1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
              0 пользователей:


              Рейтинг@Mail.ru
              [ Script execution time: 0,0318 ]   [ 15 queries used ]   [ Generated: 15.05.24, 04:44 GMT ]