C++语言中有一些多义词,例如表示静态的关键字static,将static应用在变量、函数或类成员等不同场合,它所表达的含义不一样。源程序中相同的程序元素可能会具有不同的语法解释,C++语言称这些程序元素具有多态性。C++语言有多种不同的多态形式,常见的有关键字多态、重载函数多态、运算符多态、对象多态和参数多态等。
对具有多态性的程序元素作出最终明确的语法解释,这称为多态的实现。不同多态形式具有不同的实现时间点,编译时实现的多态称为编译多态,执行时实现的多态称为执行多态。
一、关键字多态和重载多态
1、关键字多态
C++语言中的某些关键字时多义词,具有多态性,例如static
、const
、void
以及public/private/protetced
等。关键字多态是由编译器在编译源程序时根据上下文进行语法解释的,是一种编译多态。
2、重载函数多态
编译时,由编译器根据调用语句中实参的个数和类型自动调用形参最匹配的那个重载函数。形态的函数名,调用时可能会调用不同的函数,这就是重载函数多态。重载函数多态是由编译器在编译源程序时实现的,也是一种编译多态。所谓实现重载函数多态,就是在编译时将调用语句中的函数名转换成对应重载函数的内存存储地址。将源程序中的函数名转换成某个具体的函数存储地址,这种函数名到存储地址的转换被称为是对函数的绑定。
二、运算符多态
相同的运算符,计算机会根据数据类型来选择执行不同的运算,这就是运算符的多态性。运算符多态是由编译器在编译时进行语法解释的,是一种编译多态。
通过一个复数的示例来更好的理解运算符多态,下面为复数类的代码:
class Complex
{
private:
double real,image; //实数的实部和虚部
public:
Complex(double x=0,double y=0) {real = x;image = y;} //构造函数
Complex(Complex &c) {real = c.real;image = c.image; } //拷贝构造函数
void Show() { cout << real <<"+"<< image <<"i"<<endl;} //显示复数
};
重新定义C++语言已有运算符的运算规则,使同一运算符作用于不同数据类型数据时执行不同的运算,这就是运算符重载。正因为C++语言支持运算符多态,程序员才能重载运算符实现类运算。重载运算符就是以函数的形式来重新定义运算符的运算规则。其语法形式如下:
函数类型 operator 运算符(形式参数)
{ 函数体 }
为类重载运算符,可以将运算符函数定义成类额成员函数,也可以定义成类外的一个友元函数。针对不同的运算符,其运算符函数的具体实现方法也有所不同,例如单目/双目、前置/后置等。
1、双目运算符“+”
1)定义成类的成员函数
|
|
如果不将运算符函数定义成类中的函数成员,那它就时类外的普通函数。为了让类外的运算符函数能访问类中的非公有成员,那就必须将它定义成类的友元函数。
若运算符+被重载为复数类的函数成员,则调用形式为“c1.+c2”。其中c1为对象名,”.”为成员运算符,”+”为函数成员名,c2为实参。
若运算符+被重载为复数类的友元函数,则调用形式为“+(c1,c2)”。其中+为友元函数名,c1、c2均为实参。为了提高函数代码执行效率和数据保护,可以将函数成员的形参定义为常引用如Complex operator +(const Complex &c)
将形参c改为常引用。
示例:
Complex c1(1.3),c2(2,4),c3;
c3 = c1 + c2;c3.Show();//显示复数c3,显示结果:3+7i;
2、单目运算符 ++
C++语言规定:前置单目运算符重载为函数成员时没有形参,而后置单目运算符时需要有一个int型形参。这个int型形参没有参数名,在函数体中并不使用,其目的纯粹是为了使这两个重名函数具有不同形参,这样才能重载。
class Complex //通过成员函数重载++运算符
{
private:
double real,image; //实数的实部和虚部
public:
Complex(double x=0,double y=0) {real = x;image = y;} //构造函数
Complex(Complex &c) {real = c.real;image = c.image; } //拷贝构造函数
void Show() { cout << real <<"+"<< image <<"i"<<endl;} //显示复数
Complex & operator ++() //实现前置++运算符的函数成员,这里说明返回值是一个Complex的引用
{
real++; image++;
return *this;
}
Complex operator ++(int) //实现后置++运算符的函数成员
{
Cpmplex temp(*this);
real++; image++;
return temp; //返回+1之前的对象temp
}
};
示例:
Complex c1(1.3),c2,c3;
c2=++c1;c3=c2++;c1.Show();c2.Show(); //显示结果:c1为2+4i;c2为2+4i;c3为2+4i;
3、关系运算符 ==
将关系运算符 == 重载为复数类的函数成员,其代码如下:
bool Complex::operator ==(Complex c)
{
return (real=c.real && image == c.image);
}
值得注意的是,关系运算符函数的返回结果为bool型。
示例:
Complex c1(1.3),c2(2,4);
if( c1 == c2 ) //结果为false
4、赋值运算符 =
对象可以用赋值运算符 = 进行赋值。如果将赋值运算符重载为复数类的函数成员,其代码如下:
Complex & Complex::operator =(Complex c)
{
real = c.real; image = c.image;
return *this; //返回赋值后当前对象的引用
}
C++编译器通常会自动为类重载赋值运算符 “=”,程序员也可以自己编写赋值运算符函数,其功能很像拷贝构造函数。如果某个类的构造函数中动态分配了内存,除了需要为该类编写析构函数释放这些内存,程序员还要编写它的拷贝构造函数和重载赋值运算符 = ,其目的是进行深拷贝,为新建对象或别赋值对象动态再分配同样多的内存。
示例:
Complex c1(1.3),c2;
c2 = c1;c2.Show(); //显示c2为1+3i
5、右移运算符 >> 和左移运算符 <<
为类重载右移和左移运算符是为了直接使用cin和cout指令来输入和输出对象。这里注意只能将右移运算符和左移运算符定义为类外的友元函数。
friend istream & operator >>(istream &is,Complex &c)
friend ostream & operator <<(ostream &os,const Complex &c); //在类中声明友元函数
//下面为函数实现部分
istream & operator >>(istream &is,Complex &c) // 键盘依次输入复数的实部和虚部
{
is >> c.real >>c.image;
return is;
}
ostream & operator <<(ostream &os,const Complex &c) //显示器上显示复数
{
os << c.real << "+" << c.image <<"i";
return os;
}
示例:
Complex obj;
cin >>obj; //输入:3 5 [回车]
cout <<obj <<endl; //显示:3+5i
6、运算符重载的几点语法细则
1) 除了条件运算符”?:”、sizeof运算符、成员运算符”.”、指针运算符”*和作用域运算符“::”这五个运算符。C++其他运算符均可以被重载。
2)重载后,运算符的优先级和结核性不会改变。
3)重载后,运算符的操作数个数不能改变,同时至少有一个操作数是类类型。
4) 重载后,运算符的含义应与原运算符相似,否则会给使用者造成困惑。