必备技能8.3:为类增加成员函数
到目前为止,Vehicle类中只包含有数据,而没有函数。尽管只含有数据的类是完全可以使用的,但是但多数的类都是含有成员函数的。通常来说,成员函数都是用来处理类中定义的数据的,绝大多数情况下,也对外提供了访问这些数据的接口。所以,我们程序的其它部分都是通过类的成员函数来和类交互的。
我们将通过为Vehcile类增加一个成员函数来学习类成员函数的使用方法。回忆一下在前面程序的main()函数中,我们是通过把汽车每燃烧一加仑油能跑的里程数和汽车能装载的油量相乘来计算汽车最多能跑的里程数的。虽然前面的程序是完全正确的,但是这种处理方式却不是最好的。汽车最多能跑的里程数最好是通过Vehicle类自身来完成。这样做的原因是很容易理解的:汽车最多能跑的里程数取决于它的油箱的容量和它的油耗,而这两个量都是封装在Vehicle类中的。通过为Vehicle类增加一个函数来计算汽车最多能跑的里程数就使得程序具有了面向对象的结构。
给类Vehicle增加一个函数是通过在类的声明中指明函数的原型来完成的。例如,下面这个版本的Vehicle类中就指明了一个成员函数range(),用来返回汽车能跑的最多里程数:
class Vehicle
{
public:
int passengers; //载客人数
int fuelcaps; //油箱的容量
int mpg; //每加仑油能跑的英里数
int range(); //返回汽车最多能跑的里程数
}
由于类中的成员函数,比如上面的range()函数,是在定义类的时候指明了原型,所以不再需要在别的地方再声明它的原型。
在实现类的成员函数的时候,我们必须通过在成员函数的前面使用类的名字来限定的方式来告诉编译器要实现的是哪个类的成员函数。例如,下面就是一种实现range()函数的方式:
//实现vehicle类的range函数
int Vehicle::range()
{
return mpg * fuelcap;
}
注意上面的代码中用::运算符把Vehicle类和函数rang()隔开了。它叫做域解析运算符,用来告诉编译器这个函数属于哪个类。就上面的例子来说,::运算符把Vehicle类和range()函数关联起来,表明range()函数是Vehicle类的函数。多个不同名字的类是可以有相同名字的成员函数名字的。由于域解析运算符的存在,编译器就能知道哪个函数是哪个类的。
上面range()函数只有一句代码:
return mpg * fuelcap;
这个语句返回汽车油箱容量和每加仑油能跑的里程数相乘得到的最大里程数。既然Vehicle类的每一个对象都有一份fuelcap和mpg的副本,当调用函数range()的时候,实际上使用到的就是每一个对象自己的fuelcap和mpg变量的值。
在上面的函数range()中,实例变量fuelcap和mpg都是被直接引用的,而没有在其前面使用对象的名称和使用点号运算符。当一个成员函数使用其类中定义的实例变量的时候,就可以直接引用,而不需要明确指明对象和使用点号运算符。这点应该是很容易理解的。成员函数都是通过类的对象来使用的。一旦这种成员函数的调用发生了,其对象就是可知的。因此在类的成员函数中不再需要重复地指明对象了。这就是说,range()函数中的fuelcap和mpg指的就是调用函数range()的时候指定的对象中含有的实例变量的值。当然,Vehicle类之外的代码必须是通过一个对象加上点号运算符来使用range()函数的。
调用类的成员函数的时候必须指明具体的对象。这就有两种方式。第一,类的成员函数可以被类之外的代码所调用。此时,我们就必须使用对象的名字和点号运算符。例如,下面的代码调用range()函数得到的就是minivan的最大里程数:
range = minivan.range();
上面的调用表示range()函数中的计算用到的是minivan这个对象中的实例变量副本的值,因此,得到的就是minivan的最大里程数。
另外一种情况就是在一个类的成员函数中调用同类的另外一个成员函数。此时,我们可以直接调用该成员函数,而不需要使用点号运算符。这是因为编译器此时能知道需要操作的对象。只有当成员函数在它所在的类的范围之外被调用的时候才需要使用对象的名称加上点号运算符。
下面的程序在演示range()函数的同时,也给我们演示了上面说到的细节:
//使用了Vehicle类的一个程序
#include <iostream>
using namespace std;
//声明Vehicle类
class Vehicle
{
public:
int passengers; // 载客人数
int fuelcap; // 油箱容量
int mpg; // 每加仑油能跑的里程数
int range(); //计算并返回汽车能跑的最大里程数,此处为函数声明
};
//下面是range()函数的实现
int Vehicle::range()
{
return mpg * fuelcap;
};
int main()
{
Vehicle minivan; // 生成Vehicle类的一个对象
Vehicle sportscar; // 生成Vehicle类的一个对象
int range1, range2;
//给minivan对性的字段赋值
minivan.passengers = 7;
minivan.fuelcap = 16;
minivan.mpg = 21;
//给minivan对性的字段赋值
sportscar.passengers = 2;
sportscar.fuelcap = 14;
sportscar.mpg = 12;
//计算汽车加满油后能跑的最大里程数
range1 = minivan.range();
range2 = sportscar.range();
cout << "Minivan can carry " << minivan.passengers << " with a range of " << range1 << "\n";
cout << "Sportscar can carry " << sportscar.passengers << " with a range of " << range2 << "\n";
return 0;
};
上面程序的输出如下:
Minivan can carry 7 with a range of 336
Sportscar can carry 2 with a range of 168
练习:
1. 运算符::被称为什么运算符?
2. 运算符::是用来做什么的?
如果在一个类之外使用这个类的成员函数,则必须指明操作的对象并使用点号运算符。对吗?