请谨慎实现operator==操作符函数

请谨慎实现operator==操作符函数

c++中,==操作符是很有用的,但是它的实现也并非想象中的那样容易。本文将围绕一个简单的c++例子程序展开讨论,以便寻求一个简单的解决方法。

在开始讲述等于操作符之前,我们先了解一下涉及的类定义。第一个类是一个一维点定义,很简单。一个构造器和析构器,一个operator==操作符。

Definition of Class Point1D

class Point1D

{

protected:

    int xPos;

   

public:

    Point1D( int x = 0 )

        : xPos( x )

    {

    }

 

    virtual ~Point1D()

    {

    }

   

    inline bool operator==( const Point1D &rhs )

    {

        return xPos == rhs.xPos;

    }

};

第二个类是Piont2D,它是一个二维点,y轴被使用来确认相应位置。

class Point2D : public Point1D

{

protected:

    int yPos;

   

public:

    Point2D( int x = 0, int y = 0 )

        : Point1D( x ), yPos( y )

    {

    }

   

    virtual ~Point2D()

    {

    }

   

    inline bool operator==( const Point2D &rhs )

    {

        return (this == &rhs) || (xPos == rhs.xPos && yPos == rhs.yPos);

    }

};

从上面来看,两个类好像都定义的很好,真的是这样吗?让我运行一下看看结果吧。

#include <iostream>

using namespace std;


int main( void )

{

    Point1D p1d( 10 );

    Point2D p2d( 10, 20 );

    Point2D p2d2( 10, 30 );

    Point1D *pp2d1 = &p2d;

    Point1D *pp2d2 = &p2d2;

 

    if ( p1d == p2d )

        cout << "P1D is equal to P2D" << endl;

    else

        cout << "P1D is unequal to P2D" << endl;

 

    if ( *pp2d1 == *pp2d2 )

        cout << "P2D is equal to P2D2" << endl;

    else

        cout << "P2D is unequal to P2D2" << endl;

    return 0;

}

Result:

P1D is equal to P2D

P2D is equal to P2D2

WOW,居然P1DP2D是一样,P2DP2D2也是一样。显然是一个错误!错误的原因在于我们错误的实现了Point1Doperator==操作符。它没有对它的子类进行有效的检查。此外,即使Point2D也重载了==操作符,也没有使P2DP2D2能够正确的比较。其实,在多态的环境中,对于Point2D的操作符的重载往往是没有用处的,上面就是一个很好的例子。

我们现在遇到的问题是:

1)如何在Point1D中有效的对它的子类进行检查呢?

2)如何实现子类中的操作符比较函数?

对于1),我们没有办法判断,因为父类的实现代码中是无法预测子类的。有人可能会说为什么不把Point1D中的operator==声明为virtual类型,然后在子类中重置呢?它的实现代码可能类似于这样:

    virtual bool operator==( const Point1D &rhs )

    {

        const Point2D *pp2d = dynamic_cast<const Point2D *>( &rhs );

        if ( pp2d == NULL )

            return false;

        if ( this == pp2d )

            return true;

        return xPos == pp2d->xPos && yPos == pp2d->yPos;

    }  

 

到现在为止看上去好像是一个很好的主意,这样可以在使用==的时候时候清楚的识别子父类。真的是这样吗?请看下面测试结果J.

#include <iostream>

using namespace std;


int main( void )

{

    Point1D p1d( 10 );

    Point2D p2d( 10, 20 );

    Point2D p2d2( 10, 30 );

    Point1D *pp2d1 = &p2d;

    Point1D *pp2d2 = &p2d2;

 

    if ( p1d == p2d )

        cout << "P1D is equal to P2D" << endl;

    else

        cout << "P1D is unequal to P2D" << endl;

 

    if ( p2d == p1d )

        cout << "P2D is equal to P1D" << endl;

    else

        cout << "P2D is unequal to P1D" << endl;

 

    if ( *pp2d1 == *pp2d2 )

        cout << "P2D is equal to P2D2" << endl;

    else

        cout << "P2D is unequal to P2D2" << endl;

   

    return 0;

}

Result:

P1D is equal to P2D

P2D is unequal to P1D

P2D is unequal to P2D2

对于Point2D之间的比较我们很好的解决了,但是对于P1DP2D之间的比较居然出现了戏剧性的自相矛盾!仔细研究代码后,我们发现问题还是出在Point1D==操作符上,我们依然没有能够合适的识别子父类。如何解决呢?对于A=B成立的话,我们一定可以获得B=A也成立,所以我们在做Point1D==操作符的时候,一定要做两次判断等于判断,即A == B && B == A. 请看下列代码.

    virtual bool operator==( const Point1D &rhs )

    {

        return xPos == rhs.xPos && *const_cast<Point1D *>( &rhs ) == *this;

    }

Ok,让我们再次运行测试代码把。

Result:

P1D is unequal to P2D

P2D is unequal to P1D

P2D is unequal to P2D2

非常好,好象我们把问题解决了,是吗?Point1D的问题解决了,但是Point2D呢?它也存在这样的问题,我的天,这样蹩脚的代码还要在Point2D中重写一次!我简直要发疯了!难道日后所有的子类都要这样吗?!!!有没有解决方法?不要太着急,我们可以很简单的处理它。请看下面完整代码.

#include <iostream>

using namespace std;


class Point1D

{

protected:

    int xPos;

 

    virtual bool equalTo( const Point1D &rhs ) const

    {

        return xPos == rhs.xPos;

    }

   

public:

    Point1D( int x = 0 )

        : xPos( x )

    {

    }

 

    virtual ~Point1D()

    {

    }

   

    inline bool operator==( const Point1D &rhs )

    {

        return equalTo( rhs ) && rhs.equalTo( *this );

    }

   

};

 

class Point2D : public Point1D

{

protected:

    int yPos;

   

    virtual bool equalTo( const Point1D &rhs ) const

    {

        const Point2D *pp2d = dynamic_cast<const Point2D *>( &rhs );

        if ( pp2d == NULL )

            return false;

        if ( this == pp2d )

            return true;

        return yPos == pp2d->yPos && Point1D::equalTo( rhs );

    }  

 

public:

    Point2D( int x = 0, int y = 0 )

        : Point1D( x ), yPos( y )

    {

    }

   

    virtual ~Point2D()

    {

    }  

};

 

 

int main( void )

{

    Point1D p1d( 10 );

    Point2D p2d( 10, 20 );

    Point2D p2d2( 10, 30 );

    Point1D *pp2d1 = &p2d;

    Point1D *pp2d2 = &p2d2;

 

    if ( p1d == p2d )

        cout << "P1D is equal to P2D" << endl;

    else

        cout << "P1D is unequal to P2D" << endl;

 

    if ( p2d == p1d )

        cout << "P2D is equal to P1D" << endl;

    else

        cout << "P2D is unequal to P1D" << endl;

 

    if ( *pp2d1 == *pp2d2 )

        cout << "P2D is equal to P2D2" << endl;

    else

        cout << "P2D is unequal to P2D2" << endl;

   

    return 0;

}

Result:

P1D is unequal to P2D

P2D is unequal to P1D

P2D is unequal to P2D2

我们使用了equalTo函数来完成了A==B&&B==A的双向比较,在equalTo中,实现的方法和以前的完全一致,不关心比较的对象是否是父子关系。对于子类,我们仅仅重置equalTo方法就能够正确地实现了operator==方法,相当的出色,不是吗?!J

这样做的好处主要体现在以下几点:

1)能够在子类中正确地实现operator==方法,只要重置对应的equalTo就可以了。

2operator==没有使用virtual修饰,WOW,长出了一口气,对于它的很多注意点终于可以解脱了。

3)也无需为蹩脚的virtual operator==感到伤心了,因为毕竟那样做并非是真正意义上的子类==操作符。Point2D中的操作符参数应该是Point2D,而非Point1D,难道不是吗J

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值