多态
面向对象程序设计三大特征:
-
多态
-
封装
-
继承
多态是指不同的对象接收到相同的操作指令时,产生不同的动作。在程序中表现为,不同功能的函数在不同的类中具有相同的函数名,相应的类对象在调用同名函数时会执行不同的功能 -
多态按时机分为两种
编译时多态(静态联编):指程序在编译时就可确定的多态性,通过重载机制实现
运行时多态(动态联编):指必须在运行中才可以满足的多态性,通过继承和虚函数实现
虚函数(运行时多态)
若某基类函数声明为虚函数,则其公有派生类将定义与其基类虚函数
原型相同的函数,这时,当使用基类指针或基类引用操作派生类对象
时,系统会自动用派生类中的同名函数代替基类虚函数
也就是说,基类和派生类对象之间,存在赋值兼容关系
赋值兼容:
一定条件下,不同类型数据可以进行类型转换。如可以将整数赋值给
双精度变量。
基类和派生类之间也有赋值兼容关系,在使用基类对象的地方,可以使用公有派生类的对象来代替
- 派生类对象向基类对象赋值
- 派生类对象初始化基类对象引用
- 派生类对象地址赋给基类对象指针
- 调用函数时,派生类对象做实参传值基类形参或者引用
class Base
{
……};
class Derived:public Base
{
……};
Base b;Derived d;
可以有下列语句:
b=d;
Base br=&d;
Base *bp=&d;
#include<iostream>
using namespace std;
class Base
{
public:
int i;
Base (int x)
{
i=x; }
void show()
{
cout << "base= "<<i<<endl;}
};
class Derived:public Base
{
public:
Derived(int x):Base(x){
}
};
void fun(Base &b)
{
cout <<b.i<<endl;}
int main()
{
Base b1(10),*b3;
Derived d1(11),d2(12),d3(13);
b1.show()
b1=dl;b1.show();
//测试遗传亲子类赋值的赋值兼容
Base &b2=d2;b2.show();
b3=&d3;b3->show();
fun(d3);
return 0;
}
基类和派生类、基类指针、派生类指针关系
四种可能
- 基类指针指向基类对象(T)
- 派生类指针指向派生类对象(T)
- 基类指针指向派生类对象(T但受限)
- 派生类指针指向基类对象(一般不)
虚函数
虚函数的定义
虚函数是用来表现表现基类和派生类的成员函数之间的一种关系的。
虚函数定义是在基类中进行的(virtual),它提供了一种接口界面。
在基类中声明的虚函数,可以在一个或多个派生类中被重新定义。在派生类中重新定义虚函数是,都必须与基类中的原型完全相同
虚函数是一种非静态的成员函数,说明虚函数的方法如下: virtual <类型><函数名>(<参数表>)
虚函数需要注意的点:
- 在派生类中重新定义基类中的虚函数时,可以不加virtual,因为虚特性可以传递。但函数原型必须与基类中的完全相同,否则会丢失虚特性。
- 析构函数可以是虚函数,但构造函数不能是虚函数。一般来说,某类中定义有虚函数,则析构函数也应定义为虚函数。
- 在类体系中访问一个虚函数时,应使用指向基类的指针或对基类的引用,以满足运行时多态性的要求,当然也可以像调用普通成员那样利用对象名来调用一个虚函数,这时候会丢失虚特性。
- 在派生类中重新定义虚函数时,必须保证派生类中该函数的返回值和参数和基类中的说明完全一致
- 若在派生类中没有重新定义虚函数,派生类的对象将使用其基类中的虚函数代码。
- 虚函数必须是类的一个成员函数,不能是友元,但它可以是另一个类的友元。(不能进可以出)另外,虚函数不能是一个静态成员函数。
- 使用虚函数方法后,不得再使用类作用域区分符指向虚函数,这样会破坏多态性
- 若派生类中没有定义基类中已经有的虚函数,则指向该类对象的指针或者引用引用虚函数是总是引用距离最近的一个基类中的虚函数。
- 若在基类中的构造(析构)函数中也引用虚函数,则所引用的只能是本类中的虚函数,因为此时派生类中的构造(析构)函数的执行尚未完成。
#include <iostream>