操作符重载可以说是C++或者说其他面向对象语言的一个很重要的特性。它可以让对象的相关操作看起来和基本数据类型的操作一样。但是本文的重点不是介绍C++操作符重载的语法和简单的应用,而是要强调一下在应用操作符重载时,你是否真的想清楚了。什么意思呢,且看下文。
我们假定有下面这些代码:
class CObject
{
public: CObject(int iObject = 0) : m_iObject(iObject) {}
virtual ~CObject() {}
public:
bool operator ==(const CObject& rhs);
protected:
// member variables
int m_iObject;
};
class CCpp : public CObject
{public:
CCpp(int iObject, int iCpp = 1) : CObject(iObject), m_iCpp(iCpp) {}public:
bool operator ==(const CObject& rhs);
bool operator ==(const CCpp& rhs);
protected:
// CCpp's member variables
int m_iCpp;
};
class CJava : public CObject
{public:
CJava(int iObject, int iJava = 2) : CObject(iObject), m_iJava(iJava) {}public:
bool operator ==(const CObject& rhs);
bool operator ==(const CJava& rhs);
protected:
// CJava's member variables
int m_iJava;
};
可以看到,CObject基类和两个派生类CCpp和CJava,都声明了比较操作符==。下面我们把这几个比较操作符的定义用简单的几句话来实现下:
bool CObject::operator ==(const CObject& rhs)
{
return m_iObject == rhs.m_iObject;
}
bool CCpp::operator ==(const CObject& rhs)
{
const CCpp* pCpp = dynamic_cast<const CCpp*>(&rhs);
if (nullptr == pCpp) { return false; }
return (*this) == (*pCpp);
}
bool CCpp::operator ==(const CCpp& rhs)
{
return m_iObject == rhs.m_iObject && m_iCpp == rhs.m_iCpp;
}
bool CJava::operator ==(const CObject& rhs)
{
const CJava* pJava = dynamic_cast<const CJava*>(&rhs);
if (nullptr == pCpp) { return false; }
return (*this) == (*pJava);
}
bool CJava::operator ==(const CJava& rhs)
{
return m_iObject == rhs.m_iObject && m_iJava == rhs.m_iJava;
}
废话不多说,我们直接来看下如何在main函数里用这些函数:
int main()
{
CObject obj(2);
CCpp cpp(2, 3);
CJava java(2);
if (obj == cpp) { printf("obj == cpp\n"); } // 注意obj和cpp顺序
else { printf("obj != cpp\n"); }
if (cpp == java) { printf("cpp == java\n"); }
else { printf("cpp != java\n"; }
return 0;
}
显而易见,输出的结果应该是:
obj == cpp
cpp != java
写到这里,终于把引子写完了,废话多啊。下面进入正题了。我们知道,在C++项目中,像这样使用基类和派生类是很少见的,因为我们喜欢用多态。那么,我们的类中总是会存在一些虚函数。因此上面的代码可能会以下面这种形式出现:
int main()
{
CObject *pObj = nullptr, *pCpp = nullptr, *pJava = nullptr;
pObj = new CObject(2);
pCpp = new CCpp(2, 3);
pJava = new CJava(2);
if (*pObj == *pCpp) { printf("*pObj == *pCpp\n"); }
else { printf("*pObj != *pCpp\n"); }
if (*pCpp == *pJava) { printf("*pCpp == *pJava\n"); }
else { printf("*pCpp != *pJava\n"); }
}
运行下看看:
*pObj == *pCpp
*pCpp == *pJava
擦,怎么结果输出不一样了!这是神马原因(自己想想看吧
)?
所以,这里是本文要强调的一点,看似很简单的操作符重载,但是如果掉以轻心,往往结果会出人意料。
要解决这个问题,有两种方法:
- 将操作符重载成virtual类型
- 使用虚函数代替操作符重载