创建模板化对象时替代工厂模式 – C.

我想为CG项目实现一个Mesh类,但遇到了一些问题.

我想要做的是一个Mesh类,它隐藏了用户的实现细节(比如加载到特定的API:OpenGL,DirectX,CUDA ……).此外,由于Mesh类将用于研究项目,因此该Mesh类必须非常灵活.

class Channel {
  virtual loadToAPI() = 0;      
}

template <class T>
class TypedChannel : public Channel {

  std::vector<T> data;
};

template <class T>
class OpenGLChannel : public TypedChannel<T> {

  loadToAPI(); // implementation
};

class Mesh {

  template<class T>
  virtual TypedChannel<T>* createChannel() = 0; // error: no virtual template functions

  std::vector<Channel*> channels;
};

class OpenGLMesh {

  template<class T>
  TypedChannel<T>* createChannel()
  {
    TypedChannel<T>* newChannel = new OpenGLChannel<T>;
    channels.push_back(newChannel);
    return newChannel;
  };
};

为了灵活性,每个网格实际上是一个通道的集合,如一个位置通道,一个正常通道等,描述网格的某些方面.通道是std :: vector的包装器,带有一些附加功能.

为了隐藏实现细节,每个API(OpenGLMesh,DirectXMesh,CUDAMesh,…)都有一个派生类,用于处理特定于API的代码. Channels(OpenGLChannel等处理频道数据加载到API)也是如此. Mesh充当Channel对象的工厂.

但问题在于:由于Channels是模板类,createChannel必须是模板方法,而模板方法不能是虚拟方法.我需要的是像工厂模式一样用于创建模板化对象.有没有人就如何实现类似的事情提出建议?

谢谢

最佳答案 这是一个有趣的问题,但我们首先讨论编译器错误.

正如编译器所说,一个函数不能同时是虚拟和模板.要理解原因,只需考虑实现:大多数情况下,具有虚函数的对象都有一个虚拟表,它存储指向每个函数的指针.

但是对于模板,函数和类型的组合一样多:那么虚拟表应该是什么样的?在编译时不可能告诉你,你的类的内存布局包括虚拟表,必须在编译时决定.

现在问你的问题.

最简单的解决方案是每种类型只写一个虚拟方法,当然它很快就会变得单调乏味,所以让我们假装你没有听说过.

如果Mesh不应该知道各种类型,那么你肯定不需要该函数是虚拟的,因为在给定一个Mesh实例的情况下谁会知道调用该函数的类型?

Mesh* mesh = ...;
mesh.createChannel<int>(); // has it been defined for that `Mesh` ??

另一方面,我认为OpenGLMesh确实知道它将需要哪种类型的TypedChannel.如果是这样,我们可以使用一个非常简单的技巧.

struct ChannelFactory
{
  virtual ~ChannelFactory() {}
  virtual Channel* createChannel() = 0;
};

template <class T>
struct TypedChannelFactory: ChannelFactory
{
};

然后:

class Mesh
{
public:
  template <class T>
  Channel* addChannel()
  {
    factories_type::const_iterator it = mFactories.find(typeid(T).name());
    assert(it != mFactories.end() && "Ooops!!!" && typeid(T).name());

    Channel* channel = it->second->createChannel();
    mChannels.push_back(channel);
    return channel;
  } // addChannel

protected:
  template <class T>
  void registerChannelFactory(TypedChannelFactory<T>* factory)
  {
    mFactories.insert(std::make_pair(typeid(T).name(), factory));
  } // registerChannelFactory

private:
  typedef std::map < const char*, ChannelFactory* const > factories_type;
  factories_type mFactories;

  std::vector<Channel*> mChannels;
}; // class Mesh

它演示了一种非常强大的习惯,称为类型擦除.你甚至在知道这个名字之前就已经习惯了:)

现在,您可以将OpenGLMesh定义为:

template <class T>
struct OpenGLChannelFactory: TypedChannelFactory<T>
{
  virtual Channel* createChannel() { return new OpenGLChannel<T>(); }
};

OpenGLMesh::OpenGLMesh()
{
  this->registerChannelFactory(new OpenGLChannelFactory<int>());
  this->registerChannelFactory(new OpenGLChannelFactory<float>());
}

你将使用它像:

OpenGLMesh openGLMesh;
Mesh& mesh = openGLMesh;

mesh.addChannel<int>();    // fine
mesh.addChannel<float>();  // fine

mesh.addChannel<char>();   // ERROR: fire the assert... (or throw, or do nothing...)

希望我明白你的需要:p

点赞