类多态和等式运算符

时间:2015-03-31 23:02:29

标签: c++ inheritance polymorphism equality

我试图将我的头脑包围在我已经想了很长时间的事情上。 假设我有一个班级Base

class Base
{
public:
    virtual ~Base(){}
    virtual bool operator== ( const Base & rhs ) const;
};

现在,另一个类继承自它。它有两个相等运算符:

class A : public Base
{
public:
    bool operator== ( const A & rhs ) const;
    bool operator== ( const Base & rhs ) const;
private:
    int index__;
};

还有另一个类也继承自Base,并且还有两个相等运算符:

class B : public Base
{
public:
    bool operator== ( const B & rhs ) const;
    bool operator== ( const Base & rhs ) const;
private:
    int index__;
};

这是我理解的(不一定正确)。 我只能使用第一个运算符来检查相同的类对象是否相等。 然而,我可以使用第二个运算符来检查它们是否是同一类型的类,然后它们是否相等。 现在,还存在另一个类,它包含Base的指针,然而,它们是多态类型A或B。

class Z
{
public:
    bool operator== ( const Z & rhs ) const;
private:
    std::shared_ptr<Base> ptr__;
};

首先,我发现,我不能有两个运算符==重载。我没有得到编译器的错误,但是当我尝试运行它时,它只是挂起。我猜它与rtti有关,这超出了我的范围。

我一直在使用,并且非常难看,正在尝试向下转换,如果可以,那么尝试比较Z类中的实例:

bool Z::operator== ( const Z & rhs ) const
{
    if ( const auto a1 = std::dynamic_pointer_cast<A>( this->ptr__ ) )
        if ( const auto a2 = std::dynamic_pointer_cast<A>( rhs.ptr__ ) )
            return *a1 == *a2; 
    else if ( const auto b1 = std::dynamic_pointer_cast<B>( this->ptr__ ) )
        if ( const auto b2 = std::dynamic_pointer_cast<B>( rhs.ptr__ ) )
            return *b1 == *b2;
    return false;
}

这非常难看,它假定你的A和B类有一个相等运算符,它将参数作为同一类型的类。

所以我试着提出一种方法,它将使用第二种类型的操作符,更加不可知,如果你愿意则更优雅。失败了。这将需要在A和B类中使用它,从而将它从Z类移开。

bool A::operator== ( const Base & rhs ) const
{
    return ( typeid( *this ) == typeid( rhs ) ) && ( *this == rhs );
}

B类相同。这似乎不起作用(app挂起没有任何错误)。 此外,它使用某种默认运算符,还是使用基类运算符?理想情况下,它应该使用Base :: operator ==和compare类类型。

但是,如果我希望基于A级或B级成员(例如index__)进行更精细的比较,那么我显然必须为每个班级的朋友做好准备,因为当我尝试这个时,它会赢得&#39 ; t编译(除非我当然添加一个getter或以某种方式使其可见):

bool A::operator== ( const Base & rhs ) const
{
    return ( typeid( *this ) == typeid( rhs ) )
           && (*this == *rhs )
           && (this->index__ == rhs.index__ );
}

这是一个优雅,简单的解决方案吗?我是否仅限于倾斜和尝试,还是有其他方法可以实现我想要的目标?

2 个答案:

答案 0 :(得分:3)

我同意@vsoftco关于仅在基类中实现operator==并使用NVI习语。但是,我提供了一个纯虚函数,派生类需要实现它来执行相等性检查。通过这种方式,基类不知道或关心任何派生类与等效的含义。

代码

#include <iostream>
#include <string>
#include <typeinfo>

class Base
{
public:
    virtual ~Base() {}

    bool operator==(const Base& other) const
    {
        // If the derived types are the same then compare them
        return typeid(*this) == typeid(other) && isEqual(other);
    }

private:
    // A pure virtual function derived classes must implement.
    // Furthermore, this function has a precondition that it will only
    // be called when the 'other' is the same type as the instance
    // invoking the function.
    virtual bool isEqual(const Base& other) const = 0;
};

class D1 : public Base
{
public:
    explicit D1(double v = 0.0) : mValue(v) {}
    virtual ~D1() override {}

private:
    virtual bool isEqual(const Base& other) const
    {
        // The cast is safe because of the precondition documented in the
        // base class
        return mValue == static_cast<const D1&>(other).mValue;
    }

private:
    double mValue;
};

class D2 : public Base
{
public:
    explicit D2(std::string v = "") : mValue(v) {}
    virtual ~D2() override {}

private:
    virtual bool isEqual(const Base& other) const
    {
        return mValue == static_cast<const D2&>(other).mValue;
    }

private:
    std::string mValue;
};

class D3 : public Base
{
public:
    explicit D3(int v = 0) : mValue(v) {}
    virtual ~D3() override {}

private:
    virtual bool isEqual(const Base& other) const
    {
        return mValue == static_cast<const D3&>(other).mValue;
    }

private:
    int mValue;
};

int main()
{
    D1 d1a(1.0);
    D1 d1b(2.0);
    D1 d1c(1.0);

    D2 d2a("1");
    D2 d2b("2");
    D2 d2c("1");

    D3 d3a(1);
    D3 d3b(2);
    D3 d3c(1);

    std::cout << "Compare D1 types\n";
    std::cout << std::boolalpha << (d1a == d1b) << "\n";
    std::cout << std::boolalpha << (d1b == d1c) << "\n";
    std::cout << std::boolalpha << (d1a == d1c) << "\n";

    std::cout << "Compare D2 types\n";
    std::cout << std::boolalpha << (d2a == d2b) << "\n";
    std::cout << std::boolalpha << (d2b == d2c) << "\n";
    std::cout << std::boolalpha << (d2a == d2c) << "\n";

    std::cout << "Compare D3 types\n";
    std::cout << std::boolalpha << (d3a == d3b) << "\n";
    std::cout << std::boolalpha << (d3b == d3c) << "\n";
    std::cout << std::boolalpha << (d3a == d3c) << "\n";

    std::cout << "Compare mixed derived types\n";
    std::cout << std::boolalpha << (d1a == d2a) << "\n";
    std::cout << std::boolalpha << (d2a == d3a) << "\n";
    std::cout << std::boolalpha << (d1a == d3a) << "\n";
    std::cout << std::boolalpha << (d1b == d2b) << "\n";
    std::cout << std::boolalpha << (d2b == d3b) << "\n";
    std::cout << std::boolalpha << (d1b == d3b) << "\n";
    std::cout << std::boolalpha << (d1c == d2c) << "\n";
    std::cout << std::boolalpha << (d2c == d3c) << "\n";
    std::cout << std::boolalpha << (d1c == d3c) << "\n";

    return 0;
}

输出

Compare D1 types
false
false
true
Compare D2 types
false
false
true
Compare D3 types
false
false
true
Compare mixed derived types
false
false
false
false
false
false
false
false
false

答案 1 :(得分:1)

通常,在层次结构中,应该有一个公共接口,而operator==只能在Base类中使用接口的(虚拟)getter实现。否则,就像在层次结构中重新定义函数(不使用virtual)一样,这几乎总是一个坏主意。所以你可能想要考虑一下你的设计,不止一个operator==似乎很可疑。

很简单的例子:

#include <iostream>

class A
{
    int _x;
public:
    A(int x):_x(x){}
    virtual int getx() const { return _x; } // runtime
    bool operator==(const A& other){return getx() == other.getx();} // one implementation
};

class B: public A
{
    using A::A;
    int getx() const override // Make all B's equal, runtime
    {
        return 0; // so always 0, all B's are equal
    }
};

int main()
{
    A a1(10), a2(20);
    B b1(10), b2(20);
    std::cout << std::boolalpha << (a1==a2) << std::endl; // false
    std::cout << std::boolalpha << (b1==b2) << std::endl; // always true
}

这种模式通常称为the non-virtual interface idiom,是所谓的template method的表现形式(与模板无关,只是一个不幸的名称),其中有客户端(沿着层次结构)通过公共非虚拟成员函数间接调用虚函数。 Scott Meyers的第55项&#39; Effective C++对此问题进行了很好的讨论。