8.4.2 实现对运算符的完全支持
使用前面实现的operator>()运算符函数,我们仍然有许多事情不能做。用CBox对象指定问题的解决方案可能涉及像下面这样的语句:
- if(aBox > 20.0)
- // Do something...
函数不会处理这里的表达式。如果试图使用比较CBox对象与数值的表达式,那么将得到一条出错消息。为了支持该功能,需要编写另一个版本的operator>()函数作为重载函数。
要支持刚刚看到的表达式类型非常容易。类定义内的成员函数声明将如下所示:
- // Compare a CBox object with a constant
- bool operator>(const double& value) const;
该语句应出现在类定义中,>运算符的右操作数对应于这里的函数形参。作为左操作数的CBox对象是由隐式指针this传递的。
该重载运算符的实现同样很容易,函数体内只有一条语句:
- // Function to compare a CBox object with a constant
- bool CBox::operator>(const double& value) const
- {
- return this->Volume() > value;
- }
没有比这更简单的事情了,不是吗?但使用>运算符处理CBox对象仍然存在问题。我们希望写出下面这样的语句:
- if(20.0 > aBox)
- // do something...
有人可能认为,通过实现接受double类型右实参的operator<()运算符函数,然后相应改写上面这条语句,同样可以完成相同的功能,这么说非常正确。实际上无论如何,实现<运算符都是比较CBox对象所必需的。但是,在实现对某种对象类型的支持时,我们不应该人为限制在表达式中使用这种对象的方式。对象的使用应该尽可能自然。现在的问题是如何来做。
成员运算符函数总是以左边的实参作为指针this。因为本例中左边的实参是double类型,所以我们不能以成员函数的形式实现该运算符。剩下的只有两种选择:普通函数或友元函数。我们不需要访问CBox类的private成员,所以该函数不必是友元函数。因此,我们可以将左操作数属于double类型的重载>运算符实现为普通函数。由于该函数不是成员函数,其原型当然应该放在类定义的外部:
- bool operator>(const double& value, const CBox& aBox);
该函数的实现如下:
- // Function comparing a constant with a CBox object
- bool operator>(const double& value, const CBox& aBox)
- {
- return value > aBox.Volume();
- }
正如前面曾经看到的那样,普通函数(就这一点而论也包括友元函数)使用直接成员选择运算符和对象名访问对象的成员。当然,普通函数只能访问公有成员。成员函数Volume()是公有的,因此这里使用该函数没有什么问题。
如果CBox类没有公有函数Volume(),我们可以直接使用能够访问私有数据成员的友元函数,或者提供一组返回私有数据成员数值的成员函数,然后在普通函数中使用这些函数来实现比较功能。
试一试:完成>运算符的重载
我们可以在示例中将这些放在一起,以说明实际的工作过程:
- // Ex8_04.cpp
- // Implementing a complete overloaded 'greater than' operator
- #include <iostream> // For stream I/O
- using std::cout;
- using std::endl;
-
- class CBox // Class definition at global scope
- {
- public:
- // Constructor definition
- CBox(double lv = 1.0, double wv = 1.0, double hv = 1.0):
- m_Length(lv), m_Width(wv), m_Height(hv)
- {
- cout << endl << "Constructor called.";
- }
-
- // Function to calculate the volume of a box
- double Volume() const
- {
- return m_Length*m_Width*m_Height;
- }
-
- // Operator function for 'greater than' that
- // compares volumes of CBox objects.
- bool operator>(const CBox& aBox) const
- {
- return this->Volume() > aBox.Volume();
- }
-
- // Function to compare a CBox object with a constant
- bool operator>(const double& value) const
- {
- return this->Volume() > value;
- }
-
- // Destructor definition
- ~CBox()
- { cout << "Destructor called." << endl;}
-
- private:
- double m_Length; // Length of a box in inches
- double m_Width; // Width of a box in inches
- double m_Height; // Height of a box in inches
- };
-
- int operator>(const double& value, const
CBox& aBox); // Function prototype -
- int main()
- {
- CBox smallBox(4.0, 2.0, 1.0);
- CBox mediumBox(10.0, 4.0, 2.0);
-
- if(mediumBox > smallBox)
- cout << endl
- << "mediumBox is bigger than smallBox";
-
- if(mediumBox > 50.0)
- cout << endl
- << "mediumBox capacity is more than 50";
- else
- cout << endl
- << "mediumBox capacity is not more than 50";
-
- if(10.0 > smallBox)
- cout << endl
- << "smallBox capacity is less than 10";
- else
- cout << endl
- << "smallBox capacity is not less than 10";
-
- cout << endl;
- return 0;
- }
-
- // Function comparing a constant with a CBox object
- int operator>(const double& value, const CBox& aBox)
- {
- return value > aBox.Volume();
- }
示例说明
注意普通版本operator>()函数的原型所处的位置。该原型需要跟在类定义后面,因为其形参列表中要引用CBox对象。如果我们将其放在类定义前面,则该示例将不能编译。
有一种将其放在程序文件开头#include语句后面的方法:使用未完成的类声明。这样该函数的原型就可以放在类定义之前,声明语句如下所示:
- class CBox; // Incomplete class declaration
- int operator>(const double& value, CBox& aBox); // Function prototype
未完成的类声明告诉编译器CBox是一个类,这足以使编译器正确处理第二行的函数原型,因为它现在知道CBox是后面将要定义的用户定义类型。
如果有两个类,而每个类都有一个指针成员指向另一个类的对象,在此类情形中我们同样需要上述机制。这两个类都要求另一个类首先被声明,通过使用未完成的类声明就可以打破这样的僵局。
该示例的输出如下:
- Constructor called.
- Constructor called.
- mediumBox is bigger than smallBox
- mediumBox capacity is more than 50
- smallBox capacity is less than 10
- Destructor called.
- Destructor called.
在构造函数因对象smallBox和mediumBox的声明而输出消息之后,我们从三条if语句中得到三行输出,每条语句都像我们预期的那样工作。其中第一条语句调用属于类成员的运算符函数,比较CBox类的两个对象。第二条语句调用形参类型为double的成员函数。第三条if语句中的表达式调用以普通函数形式实现的运算符函数。
两个属于类成员的运算符函数亦可以普通函数的形式实现,因为它们只需要访问公有成员函数Volume()。
注意:
以实现大于运算符的相同方法,我们可以实现任何比较运算符。它们仅仅在次要细节方面不同,大体的实现方法则完全相同。