从具有不同类型的模板类创建对象

时间:2011-08-19 12:19:42

标签: c++ templates inheritance

我不确定如何制定我的问题,但这是我想要解决的难题:

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

所以任务是创建一个带有虚拟接口的对象(如果在config中指定它),否则使用真实接口类。 那我怎么声明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 个答案:

答案 0 :(得分:2)

一种方法是使用访客模式。这样,您的基类可以实现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将获得正确的类型。

答案 1 :(得分:0)

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

这种结构在多态性方面是不正确的。 两个模板实例化是两个不同的类。最好的情况是你有类似的东西:

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

但它不会给你带来任何利润。 最简单和正确的解决方案是虚拟功能。访客模式似乎也很有用。

答案 2 :(得分:0)

我实际上认为访客模式会在这里被滥用。相反,这是一种典型的开关类型代码气味,最好由多态性处理。

当你说“如果一个派生类有一个额外的方法来调用”时,那就是假设一个特定的设计。这不是功能要求。功能要求是“如果创建的两个对象中的一个必须在事件Y期间执行行为X”。为什么会有所不同?因为有许多方法可以实现这一点,不需要更多的接口(尽管可能有更多的方法)。

让我举个例子。

你有你的工厂

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,具体取决于类型。相反,总是让派生对象做出弄清楚要做什么的工作 - 因为他们知道自己是谁。如果您需要在管理对象中执行操作,请传递一个指针以供它们使用。