Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/137.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在C+中使用通用接口实现比较+;_C++_Api_Inheritance_Casting - Fatal编程技术网

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

C++ 在C+中使用通用接口实现比较+;,c++,api,inheritance,casting,C++,Api,Inheritance,Casting,假设我正在处理一个库,该库处理Item类型的项。主要入口点是一个类,如: class Worker { private: SomeContainer _c; public: void add( const Item &i ); void doSomething(); }; doSomething()方法查看添加的项,比较它们,等等,并对它们进行处理。因此,Item类需要一个操作符== 现在,我想在不同的环境中使用这个库,在每个环境中,Item类的实现都是不同的。因此

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

class Worker
{
private:
  SomeContainer _c;
public:
    void add( const Item &i );
    void doSomething();
};
doSomething()
方法查看添加的项,比较它们,等等,并对它们进行处理。因此,Item类需要一个
操作符==

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

class Item
{
protected:
    virtual bool _equals( Item &other ) = 0;
public:
    bool operator==( Item &other ) { return _equals( other ); };
};
每个环境都有自己的项实现。库只知道项目类,特定项目类在使用库的平台特定应用程序中定义和实现。在环境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;
...
};
对于每个环境,现在实现比较以供库使用的最佳方法是什么
\u equals
是在Item类中指定的,因此其特定的Item实现需要将
other
转换为它们自己的类型

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

bool AItem::_equals( Item &other )
{
    return this->_member == static_cast<AItem &>(other)._member;
}
bool AItem::_等于(项目和其他)
{
返回此->\u成员==静态\u转换(其他)。\u成员;
}
但这似乎是一个令人讨厌的解决方案,因为它允许程序员使用库来实现一个新的环境,如果他向worker添加不同类型的项,就可以破坏它

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

  • 使用
    dynamic\u cast
  • 通过向指示环境的基本项类添加成员,实现我自己的RTTI。然后,可以在比较函数中使用它来查看
    other
    是否属于同一类型。不太优雅
  • 实现库中的所有类型,并在编译时根据
    #define
    选择正确的类型。这意味着库本身必须针对每个环境进行扩展
  • 使用模板化的worker
但我觉得必须有一个更优雅的解决方案。也许是完全不同的东西。你会怎么做?

  • 如果工人是在项目上模板化的,而不是依赖继承,不是更好吗?一般来说,价值语义(而平等是价值语义的基础)不适合继承。在您的特定示例中,如果某个容器复制了它存储在其中的内容,我还担心会被截断

  • <> >如果你真的需要一个依赖于两个对象的动态类型的操作,你应该搜索“多分派”(或者看“现代C++设计”),它有一章关于这个主题。对于这一点,有几种已知的技术可以进行不同的权衡。其中一个最著名的模式通常与访问者模式相关联。最简单的变体取决于事先知道项的所有后代。(如果查看访问者模式的描述,请考虑该模式的两个层次结构对于您的应用程序都是统一的)

编辑:以下是示例的草图:

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);
}

依我看,平等取决于上下文。这意味着:环境A将在与环境B完全不同的基础上认为两个对象相等

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


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

您的最后一个选项“使用模板化工作程序”将是最好、最优雅的解决方案。它还具有最佳性能的优势,因为它避免了比较时的虚拟调用。与您的问题无关,但您确实不应该使用以下划线开头的名称。少数合法案件的规则非常复杂。前导下划线后跟大写字母或双前导下划线始终由实现保留。前导下划线后跟小写保留在全局名称空间中(在技术上是合法的,但养成不使用前导下划线的习惯会省去很多麻烦)。这是一个非常相关的问题:似乎只有少数人意识到平等不是那么简单。好,这是一个很好的折衷方案,因为尽管库需要了解类,但它们可以在使用库的特定应用程序中实现。复制的要点很好!我想Item类需要虚拟克隆功能以及虚拟析构函数。完全正确,平等测试的实现非常不同。所以你说在每个特定环境中的任何类之外都应该有一个相等函数?如果它作为函数指针提供给Worker对象,那么Worker仍然需要知道函数签名。因为它只知道Item类,所以参数只能是Item类型。因此,在equality函数中,项需要转换为正确的类型,问题本质上是相同的,不是吗?事实上,equality函数可能包含一个bool(*eq)(项,项)接口。尽管我可以想象一个系统,其中环境要求一个“类型化”工作者对象(例如,使用模板成员函数),该对象使用更具体的类型安全相等函数。