多态是指同样的消息被不同类型的对象接收时导致不同的行为。 所谓的消息是指对类的成员函数的调用,不同的行为是指不同的实现,也就是调用了不同的函数。
使用同样的“+”,就实现了整数之间、浮点数之间、双精度浮点数之间的加法,以及这几种数据类型混合的加法运算。在这里,同样的消息——相加,被不同类型的对象——变量接收后,不同类型的变量采用不同的方式进行加法运算。如果时不同类型的变量相加,例如浮点数和整型数相加,需先将整型数转换为浮点数,然后再进行加法运算,这就是典型的多态现象。
面向对象的多态性分为4类:重载多态、强制多态、包含多态和参数多态。重载多态和强制多态称为专用多态,包含多态和参数多态称为通用多态。
(1)普通函数及类的成员函数的重载属于重载多态,浮点数之间、整型数之间的加法运算也属于重载多态。
(2)强制多态是指将一个变元的类型加以变化,以符合一个函数或操作的要求,比如加法运算符在进行浮点数与整型数之间的相加时,首先进行类型强制转换,把整型数变为浮点数之后再相加的情况,就是强制多态。
(3)包含多态是类族中定义于不同类中的同名成员函数的多态行为,主要是通过虚函数来实现。
(4)参数多态与类模板相关联,在使用时,必须赋予实际的类型才可以实例化。这样由类模板实例化的各个类都具有相同的操作,而操作对象的类型却不同。
多态从实现的角度分为两类:编译时多态和运行时多态。
编译时的多态,是在编译过程中确定了同名操作的具体操作对象,而运行时的多态是在程序运行过程中才动态地确定操作所针对的具体对象。这种确定操作所针对的具体对象的过程就是绑定。绑定是指计算机程序自身彼此关联的过程,也就是把一个标识符名和一个存储地址联系在一起的过程,就是把一条消息和一个对象的方法相结合的过程。
按照绑定进行的阶段的不同,绑定分为:静态绑定和动态绑定。这两种绑定分别对应着多态的两种实现方式编译时多态和运行时多态。
静态绑定:绑定工作在编译连接阶段完成的情况。
动态绑定:绑定工作在程序运行阶段完成的情况。
C++中预定义的运算符的操作对象只能是基本数据类型。实际上对于很多用户自定义类型(比如说类),也需要有类似的运算操作。
例如,下面的程序定义了一个复数类:
class Complex
{
public:
Complex(double r = 0.0, double i = 0.0) :real(r), imag(i)
{
}
void show()const;
private:
double real;
double imag;
};
于是可以这样声明复数类对象:
Complex a(10,20),b(5,8);
接下来如果对a和b进行加法运算,写出表达式“a+b”,这时编译出错,因为编译器不知道如何完成这个加法运算。这时候就需要自己编写程序来说明“+”在作用于Complex对象时,该实现什么功能,这就是运算符重载。
运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据时导致的不同的行为。
比如说整型类型的两个数相加:
int a=2,b=3;
int c;
c=a+b;
上述代码中的c=a+b;
就相当于
c=a.+(b);
或者
c=+(a,b);
在实现过程中,先把指定的表达式转换为对运算符函数的调用,将运算对象转换成运算符函数的实参,然后根据实参类型来确定需要调用的函数,这个过程是在编译过程中完成的。
(1)规则如下:
①C++中的运算符除了少数几个之外,全部可以重载,而且只能重载C++中已经有的运算符。
②重载之后运算符的优先级和结合性都不改变。
③运算符重载是针对新类型数据的实际需要,对原有运算符进行适当的改造。一般来讲,重载的功能应当与原有的功能相类似,不能改变原运算符的操作对象的个数,同时至少有一个操作对象是自定义。
C++标准规定,有些操作符不可以重载,它们是类属关系运算符“.
”、成员指针运算符“.*
”、作用域分辨符“::
”和三目运算符“?:
”。
类属关系运算符“.
”和成员指针运算符“.*
”保证了C++中访问成员功能的含义不被改变。作用域分辨符的操作数是类型,而不是普通的表达式,也不具备重载特征。
(2)运算符重载的形式:
①重载为类的非静态成员函数
②重载为非成员函数。
运算符重载为类的非静态成员函数的一般语法形式:
返回类型 operator运算符(形参表)
{
函数体
}
运算符重载非成员函数的一般语法形式:
返回类型 operator运算符(形参表)
{
函数体
}
返回类型指定了重载运算符的返回值类型,也就是运算结果类型;operator是定义运算符重载函数的关键字;运算符是要重载的运算符名称,必须是C++中可重载的运算符,比如要重载加法运算符,这里就写“+”;形参表中给出重载运算符所需要的参数和类型。
【注意】当以非成员函数形式进行重载运算符时,有时需要访问运算符参数所涉及类的私有成员,这时可以把该函数声明为类的友元函数。
当运算符重载为类的成员函数时,函数的参数个数比原来的操作数个数要少一个(后置“++”,“–”除外);当重载为非成员函数时,参数个数与原操作数个数相同。这两种情况的参数个数有所差异的原因是,重载为类的成员函数时,第一个操作数会被作为函数调用的目的对象,因此无须出现在参数表中,在函数体中可以直接访问第一个操作数的成员;而重载非成员函数时,运算符的所有操作数必须显式通过参数传递。
(3)运算符重载的优点
可以改变现有运算符的操作方式,以用于类类型,使得程序看起来更加直观