模板类的接口

Posted

技术标签:

【中文标题】模板类的接口【英文标题】:interfaces for templated classes 【发布时间】:2009-08-10 15:25:12 【问题描述】:

我正在开发一个插件框架,它支持基础插件类CPlugin : IPlugin 的多种变体。我使用boost::shared_ptr<IPlugin> 来引用插件,除非子系统需要插件类型的特定接口。我还需要能够将插件克隆到另一个单独的对象中。这必须返回一个PluginPtr。这就是为什么CPlugin 是一个模板而不是一个直接的类。 CPlugin::Clone() 是使用模板参数的地方。以下是我正在使用的类定义:

IPlugin.h

#include "PluginMgr.h"
class IPlugin;
typedef boost::shared_ptr<IPlugin> PluginPtr;

class IPlugin

public:
  virtual PluginPtr Clone() =0;
  virtual TYPE Type() const =0;
  virtual CStdString Uuid() const =0;
  virtual CStdString Parent() const =0;
  virtual CStdString Name() const =0;
  virtual bool Disabled() const =0;

private:
  friend class CPluginMgr;
  virtual void Enable() =0;
  virtual void Disable() =0;
;

CPlugin.h

#include "IPlugin.h"
template<typename Derived>
class CPlugin : public IPlugin

public:
  CPlugin(const PluginProps &props);
  CPlugin(const CPlugin&);
  virtual ~CPlugin();
  PluginPtr Clone();

  TYPE Type() const  return m_type; 
  CStdString Uuid() const  return m_uuid; 
  CStdString Parent() const  return m_guid_parent; 
  CStdString Name() const  return m_strName; 
  bool Disabled() const  return m_disabled; 

private:
  void Enable()  m_disabled = false; 
  void Disable()  m_disabled = true; 

  TYPE        m_type;
  CStdString  m_uuid;
  CStdString  m_uuid_parent;
  bool        m_disabled;
;

template<typename Derived>
PluginPtr CPlugin<Derived>::Clone()

  PluginPtr plugin(new Derived(dynamic_cast<Derived&>(*this)));
  return plugin;

一个示例具体类 CAudioDSP.h

#include "Plugin.h"
class CAudioDSP : CPlugin<CAudioDSP>

  CAudioDSP(const PluginProps &props);
  bool DoSomethingTypeSpecific();
  <..snip..>
;

我的问题(最后)是CPluginMgr 需要更新具体类的m_disabled,但是当它通过PluginPtr 时,它无法根据模板参数确定类型和行为不同。我看不出如何避免将::Enable()::Disable() 声明为IPlugin 的私有成员,但这立即意味着应用程序的每个部分现在都需要了解CPluginMgr 类,因为它被声明为标题中的朋友。循环依赖地狱随之而来。我看到另一个选项,将启用/禁用函数声明为 CPlugin 的私有成员并改用 boost::dynamic_pointer_cast&lt;CVariantName&gt;

void CPluginMgr::EnablePlugin(PluginPtr plugin)

  if(plugin->Type == PLUGIN_DSPAUDIO)
  
    boost::shared_ptr<CAudioDSP> dsp = boost::dynamic_pointer_cast<CAudioDSP>(plugin);
    dsp->Enable();
  

然而,这会导致大量重复代码,其中包含基本 CPlugin 模板的许多变体。如果有人有更好的建议,请分享!

【问题讨论】:

为什么CPlugin 是模板化的?您应该知道一个类可以是非模板但具有模板化的成员函数(在本例中为克隆)。 也许我错过了什么,但你不能这样做:plugin-&gt;Enable(); 如果插件类型为PluginPtr?。这就是接口的意义所在…… 我不希望除 CPluginMgr 之外的任何类能够禁用/启用插件 你说得对,我应该把 CPlugin::Clone 改为模板函数 【参考方案1】:

您可以轻松编写:

class CPluginMgr;

class IPlugIn ..

    friend CPluginMgr;
    ...
;

朋友只需要一个预定义。

【讨论】:

我不敢相信它这么简单,但它是 :) 谢谢克里斯托弗!【参考方案2】:

我认为您尝试在克隆方法中返回 shared_ptr 会遇到麻烦。为什么不使用协变返回类型?您正在做的是一个常见的成语,称为 Virtual Constructor

class IPlugin

public:
    virtual IPlugin* clone() = 0;
    // ...


class CPluginMgr;

class CPlugin : public IPlugin

public:
    virtual CPlugin* clone() = 0;
    friend CPluginMgr; // as @Christopher pointed out
    void Enable(bool enable)  m_disabled = !enable; 
    // ...


class CAudioDSP : public CPlugin 

public:
    virtual CAudioDSP* clone();
    // ...


CAudioDSP* CAudioDSP::clone()

    return new CAudioDSP(*this); // assume copy constructors are properly implemented

返回一个 shared_ptr 可能会导致您出错(因为临时对象的早期销毁),我认为这通常不是一个好主意。

【讨论】:

以上是关于模板类的接口的主要内容,如果未能解决你的问题,请参考以下文章

IDEA类和方法注释模板设置

设计模式——模板方法模式

五. 面向对象高级特性5. 接口和抽象类的区别

模板类 error LNK2019: 无法解析的外部符号

根据 Derived 中的值检查基类的模板参数

使用外部类的模板参数设置内部模板类的默认模板参数