浅谈多态性与虚函数

一、多态性

在C++中多态性的表现形式之一是:具有不同功能的函数可以用同一个函数名,这样就可以实现用一个函数名调用不同内容的函数。从系统实现的角度来看,多态性分为两类:静态多态性和动态多态性。
1.静态多态性是通过函数重载(参数个数参数类型参数顺序三者中必须至少有一种不同)实现,在程序编译时系统就能决定要调用的是哪个函数,又称编译时多态性。
2.动态多态性是通过虚函数实现,在程序运行过程中才动态地确定操作所针对的对象,又称运行时多态。

二、虚函数

虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
我们通过定义一个基类与该基类的派生类来验证虚函数的特性:

#include <iostream>
#include <string>

using namespace std;

class Student//基类
{
public:
    Student(int n, string name, float s);
    void display();
protected:
    int m_num;
    string m_name;
    float m_score;
};

Student::Student(int n, string name, float s)
{
    m_num = n;
    m_name = name;
    m_score = s;
}
void Student::display()
{
    cout << "基类:\nnum:" << m_num << "\nname:" << m_name << "\nscore:" << m_score << endl;
}

class Graduate:public Student//派生类
{
public:
    Graduate(int n, string name, float s, float wage);
    void display();
private:
    float m_wage;
};

Graduate::Graduate(int n, string name, float s, float wage):Student(n, name, s),m_wage(wage)
{

}
void Graduate::display()
{
    cout << "派生类:\nnum:" << m_num << "\nname:" << m_name << "\nscore:" << m_score << "\nwage:" << m_wage << endl;
}
int main()
{
    Student stud1(100, "Li", 87.6);
    Graduate grad1(200, "Ding", 98.5, 150000);
    Student* pt = &stud1;
    pt->display();
    pt = &grad1;
    pt->display();

    return 0;
}

在以上的代码中,我们并没有使用虚函数。在主函数中定义了指向基类对象的指针变量pt,并先使pt指向基类对象stud1,通过基类指针pt调用display函数输出基类对象stud1的全部数据成员,然后使pt指向派生类对象grad1,再通过基类指针pt调用display函数输出基类对象grad1的全部数据成员。
运行结果如下:

基类:
num:100
name:Li
score:87.6
基类:
num:200
name:Ding
score:98.5

由运行结果可知基类指针pt并没有调用派生类对象grad1的display函数,而是调用了基类的display函数。如果能够用同一种方式去调用同一类族中不同类的所有同名函数,那就好了,即根据基类指向的对象调用该对象所属类的函数。
用虚函数就能实现这个想法。对程序作一点修改,在Student类中声明display函数时,在最左面加上一个关键字virtual,即

virtual void display();

这样就把Student类中的display函数声明为虚函数。程序其它部分都不改动。再编译和运行程序:

基类:
num:100
name:Li
score:87.6
派生类:
num:200
name:Ding
score:98.5
wage:150000

由虚函数实现的动态多态性就是:同一类族中不同类的对象,对同一函数调用作出不同的响应。
当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。
虚函数的实现属于函数重写,重写是指派生类函数覆盖基类函数,而与函数重写容易混淆的是函数隐藏

函数隐藏

隐藏是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

三、虚析构函数

当派生类的对象从内存中撤销时一般先调用派生类的析构函数,然后再调用派生类的构造函数。但是当我们用一个基类的指针new一个派生类的对象后,再用delete运算符撤销对象时,会发生一个情况:系统只会执行基类的构造函数,而不执行派生类的析构函数。而根据我们前面对虚函数的介绍,我们立马就会想到把基类的析构函数声明为虚函数不就行了。这样基类的指针如果指向派生类的对象,用delete销毁时便会执行派生类的析构函数。在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理。
最好把基类的析构函数声明为虚函数。这将使所有派生类的析构函数自动成为虚函数。

四、纯虚函数与抽象类

纯虚函数

是在声明虚函数时被初始化为0的函数。声明纯虚函数的一般形式为:virtual 函数类型 函数名(参数列表) = 0;
注:纯虚函数没有函数体。

抽象类

如果声明了一个类,一般可以用它定义对象。但是在面向对象程序设计中,往往有一些类,它们不用来生成对象。定义这些类的唯一目的是用它作为基类去建立派生类。它们作为一种基本类型提供给用户,用户在这个基础上根据自己的需要定义出功能各异的派生类。用这些派生类去建立对象。
这种不用来定义对象而只作为一种基本类型用作继承的类,称为抽象类(abstract class),由于它常用作基类,通常称为抽象基类(abstract base class)。凡是包含纯虚函数的类都是抽象类。因为纯虚函数是不能被调用的,包含纯虚函数的类是无法建立对象的。抽象类的作用是作为一个类族的共同基类,或者说,为一个类族提供一个公共接口。

五、结语

打个比方,汽车制造厂往往向汽车装配厂提供卡车的底盘(包括发动机、传动部分、车轮等),组装厂可以把它组装成货车、公共汽车、工程车或客车等不同功能的车辆。底本身不是车辆,要经过加工才能成为车辆,但它是车辆的基本组成部分。它相当于基类。在现代化的生产中,大多采用专业化的生产方式,充分利用专业化工厂生产的部件,加工集成为新品种的产品。生产公共汽车的厂家决不会从制造发动机到生产轮胎、制造车厢都由本厂完成。不同品牌计算机的基本部件是一样的或相似的。这种观念对软件开发是十分重要的。一个优秀的软件工作者在开发一个大的软件时,决不会从头到尾都由自己编写程序代码,他会充分利用已有资源(例如类库)作为自己工作的基础。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值