9

很抱歉,如果我的问题如此冗长且技术性很强,但我认为其他人会对它感兴趣很重要

我正在寻找一种方法来清楚地将一些软件内部与它们在 C++ 中的表示区分开来

我有一个通用参数类(稍后将存储在容器中),它可以包含 boost::any 类的任何类型的值

我有一个(大致)这种类型的基类(当然还有更多的东西)

class Parameter 
{
public:
    Parameter()
    template typename<T> T GetValue() const { return any_cast<T>( _value ); }
    template typename<T> void SetValue(const T& value) { _value = value; }
    string GetValueAsString() const = 0;
    void SetValueFromString(const string& str) const = 0;
private:
    boost::any _value;
}

派生类有两个级别:第一级定义类型和与字符串的转换(例如 ParameterInt 或 ParameterString) 第二级定义行为和真正的创建者(例如从 ParameterInt 或 ParameterFilename 派生 ParameterAnyInt 和 ParameterLimitedInt通用字符串)

根据实际类型,我想添加外部函数或根据特定参数类型运行的类,而不向基类添加虚拟方法并且不进行奇怪的强制转换

例如,我想根据参数类型创建适当的 gui 控件:

Widget* CreateWidget(const Parameter& p)

当然,除非我使用 RTTI 或我自己实现它(使用枚举和 switch case),否则我无法从中理解真正的参数类型,但这不是正确的 OOP 设计解决方案,你知道的。

经典的解决方案是访问者设计模式http://en.wikipedia.org/wiki/Visitor_pattern

这种模式的问题是我必须提前知道将实现哪些派生类型,所以(将维基百科和我的代码中写的内容放在一起)我们将有以下几种:

struct Visitor 
{
  virtual void visit(ParameterLimitedInt& wheel) = 0;
  virtual void visit(ParameterAnyInt& engine) = 0;
  virtual void visit(ParameterFilename& body) = 0;
};

是否有任何解决方案可以以任何其他方式获得此行为,而无需事先知道所有具体类型并且无需派生原始访问者?


编辑: Pizza 博士的解决方案似乎最接近我的想法,但问题仍然存在,并且该方法实际上依赖于 dynamic_cast,我试图将其作为一种(即使很弱)RTTI 方法来避免

也许最好在不引用访问者模式的情况下思考一些解决方案并清理我们的思想。目的只是具有以下功能:

Widget* CreateWidget(const Parameter& p)

每个“具体”参数的行为不同,而不会丢失其类型的信息

4

5 回答 5

4

对于Vistor的通用实现,我建议使用Loki Visitor,它是Loki 库的一部分。

于 2008-08-29T00:02:18.950 回答
1

我用这个(“非循环访问者”)效果很好;在某种程度上,它可以在不更改现有类的情况下向层次结构添加新类。

于 2008-08-28T10:06:48.740 回答
0

如果我正确理解这一点...

我们有一个可以使用不同硬件选项的对象。为此,我们使用了 Device 的抽象接口。Device 有一堆函数可以在某些事件上触发。用途是相同的,但设备的各种实现要么具有完整的功能,要么立即返回。为了让生活更轻松,这些函数是无效的,并在出现问题时抛出异常。

于 2008-08-28T09:58:49.867 回答
0

为了完整起见:

当然,完全可以为您的对象编写自己的多方法指针表实现,并在运行时手动计算方法地址。Stroustrup 有一篇关于实现多方法主题的论文(尽管在编译器中)

我真的不会建议任何人这样做。让实现运行良好是相当复杂的,使用它的语法可能会非常笨拙且容易出错。但是,如果其他一切都失败了,这可能仍然是要走的路。

于 2008-08-28T10:36:55.127 回答
0

我无法理解您的要求。但我会说——用我自己的话来说——我理解的情况是:

  • 你有抽象的参数类,它最终被子类化为一些具体的类(例如:ParameterLimitedInt)。

  • 您有一个单独的 GUI 系统,它将以通用方式传递这些参数,但问题是它需要呈现特定于参数类的具体类型的 GUI 组件。

  • 限制是您不想执行 RTTID,并且不想编写代码来处理每种可能类型的具体参数。

  • 您可以使用访问者模式。

有了这些是您的要求,我将如何处理这种情况:

我将实现接受()返回布尔值的访问者模式。基本 Parameter 类将实现一个虚拟的 accept() 函数并返回 false。

Parameter 类的具体实现将包含accept() 函数,该函数将调用访问者的visit()。他们会返回真。

访问者类将使用模板化的 visit() 函数,因此您只需覆盖您希望支持的具体参数类型:

class Visitor
{
public:
  template< class T > void visit( const T& param ) const
  {
    assert( false && "this parameter type not specialised in the visitor" );
  }
  void visit( const ParameterLimitedInt& ) const; // specialised implementations...
}

因此,如果 accept() 返回 false,则您知道 Parameter 的具体类型尚未实现访问者模式(如果您希望根据具体情况处理其他逻辑)。如果访问者模式中的 assert() 触发,那是因为它没有访问您已经实现专门化的 Parameter 类型。

所有这一切的一个缺点是不支持的访问仅在运行时被捕获。

于 2008-08-28T21:31:24.897 回答