создание объектов из шаблонного класса другого типа - PullRequest
1 голос
/ 19 августа 2011

Я не совсем знал, как сформулировать свой вопрос, но вот загадка, которую я пытаюсь решить:

if (config.a)
  myObject = new Object<DummyInterface>();
else
  myObject = new Object<RealInterface>();

, поэтому задача состоит в создании объекта с фиктивным интерфейсом, если он указанв конфигурации, в противном случае используйте реальный класс интерфейса.Как мне объявить myObject?есть пара вариантов, я мог бы иметь класс Object для наследования от абстрактного класса без шаблонов: то есть:

class Base
{
   ...
}

template <class T>
class Object : public Base 
{
...
}

Тогда я мог бы объявить myObject как:

Base* myObject;

Но вот проблема: что если мой класс Object объявляет не виртуальный метод:

template <class T>
class Object : public Base 
{
 public:
   T getInterface() { return myInterface;}
 private:
   T myInterface;
}

Я не могу назвать его так:

myObject->getInterface()

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

Есть предложения как это обойти?Может быть, есть другое решение?

Ответы [ 3 ]

2 голосов
/ 19 августа 2011

Одним из способов является использование шаблона посетителя. Таким образом, ваш базовый класс может реализовать метод visit(), а ваши производные экземпляры могут переопределить ...

Например ..

SomeComponent
{
  template <typename T>  // I'm being lazy here, but you should handle specific types
  void handle(T& cInst)
  {
    // do something
  }
};

class Base
{
public:
  virtual void visit(SomeComponent& cComp) = 0;
};

template <class T>
class Object : public Base 
{
public:
  virtual void visit(SomeComponent& cComp)
  { 
    cComp.handle(*this);
  }
};

Теперь вы можете сделать это

SomeComponent c;
Base* obj = new Object<int>;
obj->visit(c);

И c получит правильный тип.

0 голосов
/ 19 августа 2011

Я действительно думаю, что здесь будет неправильно использоваться шаблон посетителей. Вместо этого это классический запах кода включаемых типов, который лучше всего обрабатывается полиморфизмом.

Когда вы говорите «что, если у одного производного класса есть дополнительный метод для вызова», это предполагает конкретный дизайн. Это не функциональное требование. Функциональным требованием было бы «что если один из двух созданных объектов должен был вести себя как X во время события Y». Почему это отличается? Потому что есть ряд способов реализовать это, которые не требуют больше интерфейса (хотя, может быть, больше методов).

Позвольте мне показать пример.

У вас есть фабрика

std::map<ConfigValue, Generator> objectFactory_;

Для которого вы зарегистрировали группу генераторов (вероятно, в конструкторе класса)

RegisterGenerator(configValueA, DummyGenerator);
RegisterGenerator(configValueB, RealGenerator);
...

И в какой-то момент вы хотите создать один из этих объектов.

shared_ptr<Base> GetConfigObject(ConfigFile config)
{
  return objectFactory_[config.a]();
}

А затем вы хотите использовать объект для обработки события, вы можете сделать

void ManagingClass::HandleEventA()
{
  theBaseObjectReturned->HandleEventAThroughInterfaceObject(this);
}

Обратите внимание, как я передал этот указатель. Это означает, что если у вас есть один объект, который не хочет ничего делать (например, выполнить дополнительный вызов поведения), который может предоставить ваш управляющий класс, он не должен использовать это.

Object<DummyInterface>::HandleEventAThroughInterfaceObject(ManagingClass *)
{
  // just do dummy behavior
}

И затем, если вы хотите сделать что-то дополнительное (вызвать новое поведение), оно может сделать это через этот указатель в RealInterface

Object<RealInterface>::HandleEventAThroughInterfaceObject(ManagingClass * that)
{
  that->DoExtraBehavior();
  // then dummy - or whatever order
  // you could even call multiple methods as needed
}

Это основной подход, который вы всегда должны использовать при работе с полиморфизмом. У вас никогда не должно быть двух разных путей кода для разных типов, кроме как через вызовы виртуальной диспетчеризации. У вас никогда не должно быть двух разных блоков кода, один из которых вызывает методы A, B и C, а другой - только A и D при работе с базовым объектом, в зависимости от типа. Вместо этого всегда заставляйте производные объекты выполнять работу, выясняя, что делать - потому что они знают, кто они. Если вам нужно что-то сделать в управляющем объекте, передайте указатель this для работы с ним.

0 голосов
/ 19 августа 2011
if (config.a)
  myObject = new Object<DummyInterface>();
else
  myObject = new Object<RealInterface>();

Эта конструкция неверна с точки зрения полиморфизма.Два экземпляра шаблона - это два разных класса.Лучшая ситуация, когда у вас есть что-то подобное:

template <class T> SomeClass: public SomeBaseClass 
{
};
......... 
SomeBaseClass* myObject;

Но это не приносит вам никакой прибыли.Самое простое и правильное решение - виртуальные функции.Шаблон посетителя тоже кажется полезным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...