使用C ++中的通用接口实现比较

时间:2009-07-26 10:52:14

标签: c++ api inheritance casting

假设我正在处理一个处理Item类型项目的库。主入口点是类:

class Worker
{
private:
  SomeContainer _c;
public:
    void add( const Item &i );
    void doSomething();
};

doSomething()方法查看添加的项目,比较它们等,并对它们做些什么。所以Item类需要operator==

现在,我想在不同的环境中使用这个库,并且在每个环境中,Item类的实现都是不同的。因此,Item需要一个虚拟比较函数:

class Item
{
protected:
    virtual bool _equals( Item &other ) = 0;
public:
    bool operator==( Item &other ) { return _equals( other ); };
};

每个环境都有自己的Item实现。该库只知道Item类,并且使用库在特定于平台的应用程序中定义和实现特定的项类。在环境A中它可能是:

class AItem: public Item
{
private:
    bool _equals( Item &other );
    std::string _member;
...
};

在环境B中:

class BItem: public Item
{
private:
    bool _equals( Item &other );
    int _member;
...
};

现在,对于每个环境,实现比较以供库使用的最佳方法是什么? Item类中指定了_equals,因此其特定的项目实现需要将other强制转换为自己的类型。

在给定的环境中,不会同时使用不同的项目类型,因此,假设以下情况,以下将是安全的:

bool AItem::_equals( Item &other )
{
    return this->_member == static_cast<AItem &>(other)._member;
}

但它似乎是一个令人讨厌的解决方案,因为它允许程序员使用该库来实现一个新的环境,如果他向工作者添加不同类型的项目,就会破坏它。

我能想到的其他解决方案是:

  • 使用dynamic_cast
  • 通过向指示环境的基类Item类添加成员来实现我自己的RTTI排序。然后可以在比较函数中使用它来查看other是否属于同一类型。不是很优雅。
  • 实现库中的所有类型,并在编译时根据#define选择正确的类型。这意味着必须为每个环境扩展库本身。
  • 使用模板化工人。

但我觉得必须有一个更优雅的解决方案。也许完全不同的东西。你会做什么?

2 个答案:

答案 0 :(得分:4)

  • 如果工人在Item上模板化而不是依赖继承,那会不会更好?一般来说,价值语义(和平等在价值语义的地球上)并不适用于继承。在您的具体示例中,如果SomeContainer复制存储在其中的内容,我也担心会截断。

  • 如果你真的需要一个依赖于两个对象的动态类型的操作,你应该搜索“多个调度”(或者查看“现代C ++设计”,它有一个关于这个主题的章节)。有几种已知技术可用于不同的权衡。最着名的一个通常与访客模式相关联。最简单的变体取决于知道Item的所有后代。 (如果您查看访问者模式的描述,请考虑该模式的两个层次结构将为您的应用程序统一)。

编辑:这是一个例子的草图:

class ItemA;
class ItemB;

class Item
{
public:
    virtual bool equal(Item const&) const = 0;
protected:
    virtual bool doesEqual(ItemA const*) const { return false; }
    virtual bool doesEqual(ItemB const*) const { return false; }
};

class ItemA: public Item
{
public:
    virtual bool equal(Item const& other) const
    {
        return other.doesEqual(this);
    }
protected:
    virtual bool doesEqual(ItemA const* other) {
        return do_the_check;
    }
};

class ItemB: public Item
{
public:
    virtual bool equal(Item const& other) const
    {
        return other.doesEqual(this);
    }
protected:
    virtual bool doesEqual(ItemB const* other) {
        return do_the_check;
    }
};

bool operator==(Item const& lhs, Item const& rhs)
{
    return lhs.equal(rhs);
}

答案 1 :(得分:0)

Imho,平等依赖于上下文。这意味着:环境A将两个物体视为完全不同于环境B的地面。

因此,我认为环境应该提供平等功能。

平等的另一个方面是它通常被认为是对称操作:如果a等于b,b等于a。这意味着它不能是单一调度,即实现为类方法。因此,它只需要依赖于两个对象的接口。这也强制使用环境提供的显式双参数相等函数。