Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Как реализовать полиморфное поведение при заранее неизвестном прототипе функции?

87K
28 мая 2015 года
Rseny
8 / / 28.05.2015
hi!

Опишу проблему подробнее. Допустим есть базовый класс Base, и вся работа с его подклассами будет осуществляться через указатель на Base.
 
Код:
class Base {
 public:
  virtual void f() {}
};
А так же:
 
Код:
class Derived : public Base {
 public:
  void f(int x) {}
};
Как сделать так, чтобы при
 
Код:
Base *obj = new Derived;
obj->f();
Вызывалась функция f из Derived?

Есть подозрения, что я подхожу не правильно к проектированию. Изначально задача ставится так: есть абстрактный класс Module, от которого наследуется несколько различных классов для работы с АЦП, почти все они имеют один и тот же интерфейс, но есть специфические различия: например они имеют разные параметры (тип, количество) (можно было бы получать их в конструкторе, но их нужно изменять и в течении жизни объекта). Далее нужно работать с этими АЦП через единый интерфейс (Module), как реализовать такое поведение?

Заранее спасибо!
  • Вы бы лучше интерфейсы описали. от Ramon, 28 мая 2015 года
446
28 мая 2015 года
Meander
487 / / 04.09.2011
Как Вы знаете чисто виртуальные функции объявляются, когда дизайнер интерфейса классов хочет вынудить разработчика класса-потомка переопределить поведение этого класса, но так, чтобы сохранились сигнатуры методов. У Вас, очевидно, сигнатуры будут меняться у каждого из потомков, а значит это разные функции и в общем разные интерфейсы (их роднит, только, имя SetupChannel). Через указатель на базовый класс можно обратиться к методам потомков, если их имена уже известны на стадии проектирования базового класс, что Вас не устраивает. Значит надо обеспечить универсальность сигнатуры, например, с помощью вспомогательного класса, указатель на экземпляры которого будет передаваться как параметр функции SetupChannel.

А можно сделать так, как сказал Ramon, добавлять в классы потомки нужные поля, а метод будет иметь сигнатуру void SetupChannel(void).
Код:
#include <stdint.h>
#include <iostream>
#include <stdlib.h>

typedef int16_t WORD;

class Param {
  public:
    int    ChannelNum;
    WORD   parameters;
    double delay;
};

class Base {
 public:
  virtual void SetupChannel(Param *parameter) = 0;
};

class Derived1 : public Base {
 public:
  virtual void SetupChannel(Param *p) {
    std::cout << "Derived1" << std::endl;
    std::cout << "ChannelNum : " << p->ChannelNum << std::endl;
    std::cout << "parameters : " << p->parameters << std::endl;
  }
};

class Derived2 : public Base {
 public:
  virtual void SetupChannel(Param *p) {
    std::cout << "Derived2" << std::endl;
    std::cout << "ChannelNum : " << p->ChannelNum << std::endl;
    std::cout << "delay : " << p->delay << std::endl;
  }
};

int main(int argc, char** argv) {
  Base    *B;
  Param    P1,
           P2;
  Derived1 D1;
  Derived2 D2;
 
  P1.ChannelNum = 1;
  P1.parameters = 'Ù';
 
  P2.ChannelNum = 2;
  P2.delay      = 0.568;
 
  B = &D1;
  B->SetupChannel(&P1);
  B = &D2;
  B->SetupChannel(&P2);
 
  system("pause");
    return 0;
}
446
29 мая 2015 года
Meander
487 / / 04.09.2011
Вообще, Вы - как дизайнер базового класса, столкнулись с трудностями характерными для всех дизайнеров. Вы увидели, что выбранный интерфейс не универсален, не дальновиден и неудачен. Даже Вы - дизайнер базового класса и классов потомков в одном лице видите недостатки своего-же подхода. Это явный признак того, что следует продумать дизайн получше.
87K
28 мая 2015 года
Rseny
8 / / 28.05.2015
Хорошо, к примеру так:
Код:
class Base {
 public:
  virtual void SetupChannel(int ChannelNum, <тут могут появиться аргументы, на данный момент их тип и кол-во не известено> )
};

class Derived1 : public Base {
 public:
  virtual void SetupChannel(int ChannelNum, WORD parameters) {
    //
  }
};

class Derived2 : public Base {
 public:
  virtual void SetupChannel(int ChannelNum, double delay) {
    //
  }
};
1
28 мая 2015 года
kot_
7.3K / / 20.01.2000
Цитата:
virtual void SetupChannel(int ChannelNum, <тут могут появиться аргументы, на данный момент их тип и кол-во не известено> )
};

Когда они появятся - это будет ДРУГАЯ виртуальная функция.

87K
28 мая 2015 года
Rseny
8 / / 28.05.2015
Цитата: kot_
Цитата:
virtual void SetupChannel(int ChannelNum, <тут могут появиться аргументы, на данный момент их тип и кол-во не известено> )
};

Когда они появятся - это будет ДРУГАЯ виртуальная функция.

Я понимаю это, как можно решить такую проблему? Думаю я не первый с этим сталкиваюсь, может быть нужно смотреть в сторону какого-либо паттерна?

260
28 мая 2015 года
Ramon
1.1K / / 16.08.2003
Очевидно, что вы не ф-цию вызываете, а объект создаете ну вот и создавайте объект.
87K
28 мая 2015 года
Rseny
8 / / 28.05.2015
Цитата: Meander
Как Вы знаете чисто виртуальные функции объявляются, когда дизайнер интерфейса классов хочет вынудить разработчика класса-потомка переопределить поведение этого класса, но так, чтобы сохранились сигнатуры методов. У Вас, очевидно, сигнатуры будут меняться у каждого из потомков, а значит это разные функции и в общем разные интерфейсы (их роднит, только, имя SetupChannel). Через указатель на базовый класс можно обратиться к методам потомков, если их имена уже известны на стадии проектирования базового класс, что Вас не устраивает. Значит надо обеспечить универсальность сигнатуры, например, с помощью вспомогательного класса, указатель на экземпляры которого будет передаваться как параметр функции SetupChannel.
Код:
#include <stdint.h>
#include <iostream>
#include <stdlib.h>

typedef int16_t WORD;

class Param {
  public:
    int    ChannelNum;
    WORD   parameters;
    double delay;
};

class Base {
 public:
  virtual void SetupChannel(Param *parameter) = 0;
};

class Derived1 : public Base {
 public:
  virtual void SetupChannel(Param *p) {
    std::cout << "Derived1" << std::endl;
    std::cout << "ChannelNum : " << p->ChannelNum << std::endl;
    std::cout << "parameters : " << p->parameters << std::endl;
  }
};

class Derived2 : public Base {
 public:
  virtual void SetupChannel(Param *p) {
    std::cout << "Derived2" << std::endl;
    std::cout << "ChannelNum : " << p->ChannelNum << std::endl;
    std::cout << "delay : " << p->delay << std::endl;
  }
};

int main(int argc, char** argv) {
  Base    *B;
  Param    P1,
           P2;
  Derived1 D1;
  Derived2 D2;
 
  P1.ChannelNum = 1;
  P1.parameters = 'Ù';
 
  P2.ChannelNum = 2;
  P2.delay      = 0.568;
 
  B = &D1;
  B->SetupChannel(&P1);
  B = &D2;
  B->SetupChannel(&P2);
 
  system("pause");
    return 0;
}

Понятно, а есть ли более красивые варианты организации вспомогательного класса? Мне не нравится, что для каждого конкретного модуля будут неиспользуемые параметры (для Derived1 - delay, для Derived2 - parameters).

Спасибо!

446
28 мая 2015 года
Meander
487 / / 04.09.2011
Мудрый Ramon, действительно предложил более элегантное решение:
Код:
#include <stdint.h>
#include <iostream>
#include <stdlib.h>

typedef int16_t WORD;

class Base {
 public:
   int          ChannelNum;
   virtual void SetupChannel(void) = 0;
};

class Derived1 : public Base {
 public:
   WORD         parameters;
   virtual void SetupChannel(void) {
     std::cout << "Derived1" << std::endl;
     std::cout << "ChannelNum : " << ChannelNum << std::endl;
     std::cout << "parameters : " << parameters << std::endl;
   }
};

class Derived2 : public Base {
 public:
   double       delay;
   virtual void SetupChannel(void) {
     std::cout << "Derived2" << std::endl;
     std::cout << "ChannelNum : " << ChannelNum << std::endl;
     std::cout << "delay : " << delay << std::endl;
   }
};

int main(int argc, char** argv) {
  Base    *B;
  Derived1 D1;
  Derived2 D2;
 
  D1.ChannelNum = 1;
  D1.parameters = 'Ù';
 
  D2.ChannelNum = 2;
  D2.delay      = 0.568;
 
  B = &D1;
  B->SetupChannel();
  B = &D2;
  B->SetupChannel();
 
  system("pause");
    return 0;
}
87K
28 мая 2015 года
Rseny
8 / / 28.05.2015
Это хорошее решение, оно мне нравится. Но есть необходимость изменять параметры в любое время жизни объекта.
446
28 мая 2015 года
Meander
487 / / 04.09.2011
Цитата: Rseny
Это хорошее решение, оно мне нравится. Но есть необходимость изменять параметры в любое время жизни объекта.

В этом нет проблем, просто присваивайте новые значения соответствующим полям экземпляров классов в любое удобное время.

87K
28 мая 2015 года
Rseny
8 / / 28.05.2015
Цитата: Meander
Цитата: Rseny
Это хорошее решение, оно мне нравится. Но есть необходимость изменять параметры в любое время жизни объекта.

В этом нет проблем, просто присваивайте новые значения соответствующим полям экземпляров классов в любое удобное время.

Как мне это сделать пользуясь интерфейсом базового класса?

260
28 мая 2015 года
Ramon
1.1K / / 16.08.2003
Цитата: Rseny
Цитата: Meander
Цитата: Rseny
Это хорошее решение, оно мне нравится. Но есть необходимость изменять параметры в любое время жизни объекта.

В этом нет проблем, просто присваивайте новые значения соответствующим полям экземпляров классов в любое удобное время.

Как мне это сделать пользуясь интерфейсом базового класса?

Создавайте объект с характерными для канала параметрами в конструкторе/убивайте объект канала.

87K
28 мая 2015 года
Rseny
8 / / 28.05.2015
Понятно.

Большое спасибо за ответы!
446
28 мая 2015 года
Meander
487 / / 04.09.2011
Никак. Придется делать так:
 
Код:
B = &D2;
  for(int i=2;i<8;i++){
    D2.ChannelNum = i;
    B->SetupChannel();
  }
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог