假设我正在处理一个处理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
。other
是否属于同一类型。不是很优雅。#define
选择正确的类型。这意味着必须为每个环境扩展库本身。但我觉得必须有一个更优雅的解决方案。也许完全不同的东西。你会做什么?
答案 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)
因此,我认为环境应该提供平等功能。
平等的另一个方面是它通常被认为是对称操作:如果a等于b,b等于a。这意味着它不能是单一调度,即实现为类方法。因此,它只需要依赖于两个对象的接口。这也强制使用环境提供的显式双参数相等函数。