何为多态性?
相同的函数在不同环境下功能不同,例如‘<<‘在标准输入输出里是流,在运算中则是移位。’+‘在俩整数之间是直接相加,当一个数为浮点数,一个为整数时,则还要将整数转化为浮点数再相加。
面对对象的多态性可以分为四种
1)重载多态
如类的成员函数重载。
2)强制多态
如’+‘运算符。将类型强制转化
3)包含多态
同样的操作可用于一个类型及其子类型。
4)参数多态
通过给出不同的类型参数,使得一个结构有多种类型
C++多态的表现形式主要有函数的重载,运算符的重载和虚函数。
函数重载
C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个运算符完成不同的运算功能。
这就是重载函数。重载函数常用来实现功能类似而所处理的数据类型不同的问题。
#include<iostream>
using namespace std;
class Clock
{
public:
Clock(int newH, int newM, int newS); // 构造函数
Clock() //构造函数,重载
{
hour=0;
minute=0;
second=0;
}
void setTime(int newH, int newM, int newS);
void showTime(){
cout<<hour<<" "<<minute<<" "<<second<<endl;
}
private:
int hour,minute,second;
};
Clock::Clock(int newH, int newM, int newS)
{
hour=newH;
minute=newM;
second=newS;
}
int main()
{
Clock m1; //参数不同,功能不同
Clock m2(1,2,3);
m1.showTime();
m2.showTime();
}
结果
0 0 0
1 2 3
运算符重载
运算符函数定义的一般格式为
数据类型 operator<运算符符号>(<参数表>)
{
<函数体>
}
以下运算符不能重载
1)对象访问运算符 " . "
2)成员指针原算符 " .* "
3)作用域运算符 " :: "
4) " sizeof " 运算符
5) 三目运算符 “ ? ”
合法的运算符重载有四个限制
1) 重载以后的运算符不能改变运算符的优先级和结合性
2)重载以后的运算符不能改变运算符操作数的个数及语法结构
3)重载以后的运算符操作数至少有一个是自定义类型的。
4)重载运算符函数通常不能有默认的参数
#include<iostream>
using namespace std;
class A{
public:
A(int a=0,int b=0):a(a),b(b){}
int a;
int b;
A operator+(A c) //+运算符重载
{
return A(a+c.b,b+c.a);
}
void show()
{
cout<<a<<" "<<b<<endl;
}
};
int main()
{
A a0(10,2);
A a1(3,8);
A a2=a0+a1;
a2.show();
return 0;
}
结果
18 5
虚函数
不同于其二者,虚函数是动态多态,而函数重载和运算符重载是静态重载。
静态多态在编译时就能知道函数调用地址,而动态多态须在运行时才知道调用地址。
运行时多态性须满足三个条件
1)父类的包含虚函数
2)子类的虚函数必须和父类的虚函数声明一致
3)通过父类的指针,或者引用访问虚函数实现动态多态
虚函数声明格式
virtual 返回类型 函数名(形参表)
{
函数体
}
虚函数的几个特点
1)当父类的析构函数被定义为虚函数,那么其派生类的析构函数也为虚函数
2)使用虚函数时,存在子类公有继承父类才有意义
3)当父类的函数被定义为虚函数,那么其派生类里的同名函数也为虚函数
4)虚函数必须是所在类的成员函数,不能是友元函数,也不能是静态成员函数
5)当类中包含虚函数时,不管虚函数有几个,他们一共只占4个字节大小
#include<iostream>
using namespace std;
class A{
public:
int a;
A(){
cout<<"begin A"<<endl;
a=1;
}
void show(){
cout<<"A.a="<<a<<endl;
}
virtual ~A(){ //虚函数
cout<<"over A"<<endl;
}
};
class C:public A{
public:
C(){
cout<<"begin C"<<endl;
a=22;
}
~C(){
cout<<"over C"<<endl;
}
};
int main()
{
A *p=new C; //C类无名对象
p->show();
delete p; //回收无名对象
}
结果
begin A
begin C
A.a=22
over C
over A
这里我们使A类的析构函数为虚函数,倘若A类的析构函数不是虚函数,则结果不同
begin A
begin C
A.a=22
over A
可以看到,其派生类的析构函数没有运行到;
因为类中的虚函数,当访问到时,为一起访问其派生类的同名覆盖函数。
纯虚函数
纯虚函数是虚函数的一种特例,包含纯虚函数的类称为抽象类,其不可以定义对象,只能作为父类。
其形式一般为
virtual 返回值类型 函数名 (参数表)=0;