多态性与虚函数

多态可分为编译时多态和运行时的多态,运算符重载就属于编译时多态,本章主要讨论函数重载和建立在虚函数基础上的运行时的多态。

5.1 多态性

生活中也常存在多态性,例如学校的上课铃响了,这时,教师会去上课,学生会回到教室,校广播站人员会关掉广播。不同人员对同一事件产生不同的行为,这就是多态性在日常生活中的表现。

面向对象程序设计中的多态性,指的是不同的对象在得到相同的消息时产生了不同的行为,也就是说不同的对象以自己的方式去响应相同的消息。消息主要指的是函数的调用,不同的行为是指不同的函数。

5.1.1 多态的类型

静态多态性是系统在程序编译过程中根据函数形参的不同来决定到底调用哪个函数,因而也被称为编译时的多态,例如编译时多态。

动态多态性则是程序在编译、链接过程中还无法确定要调用哪个函数,必须要等到运行时才能确定具体的操作的对象,需要通过虚函数来实现。

5.1.2 派生类重载基类函数
#include<iostream>
using namespace std;

class Animal {
private:
    string name;
public:
    Animal(string n) {
        name = n;
    }
    void enjoy() {
        cout << "叫声" << endl;
    }
};
class Cat :public Animal {
public:
    string eyesColor;
    Cat(string n, string c) :Animal(n) {
        eyesColor = c;
    }
    void enjoy() {
        cout << "喵喵" << endl;
    }
    void enjoy(int i) {
        for (; i >= 1; i--) cout << "";
        cout << endl;
    }
};
void a() {
    Animal a("cat");
    Cat c("catname", "blue");
    a.enjoy();
    c.enjoy();
    c.enjoy(5);
    c.Animal::enjoy();
}
int main() {
    a();
    getchar();
    return 0;
}
-------------------------------------------------------------------------
叫声
喵喵
喵喵喵喵喵
叫声
View Code

下面把基类成员函数在派生类中重载的编译区分方法做一个总结:

(1)参数的特征,即根据参数的类型、个数、顺序的不同来加以区分,如c.enjoy()和c.enjoy(5).

(2)使用“::”加以区分,如c.Animal::enjoy()。

(3)根据类对象加以区分,如a.enjoy()和c.enjoy()。

5.1.3 联编

多态的实现通过联编来完成,在执行多态语句时,必须确定要执行什么样的行为,也就是确定调用哪个函数。

(1)静态联编:在程序开始运行之前的编译、连接过程中,系统就能确定多态性语句要调用哪个函数的过程称为静态联编,如函数重载和运算符重载。

(2)动态联编:如果要在程序运行时才能完成,这时的联编被称为动态联编或晚期联编,需要通过虚函数来实现

 

5.2 虚函数

int main() {
    Cat c("catname", "blue");
    Animal *pet;
    pet = &c;
    pet->enjoy();
    return 0;
}
--------------------------------------------------------------------------
叫声

实验证明:基类对象的指针虽然可以用派生类对象的地址进行初始化,但不能引用派生类的成员函数,因为这种关系是在编译时完成的,因此不是运行时的多态,而是静态多态性的体现。

下面引入虚函数的声明:

#include<iostream>
using namespace std;

class Animal {
private:
    string name;
public:
    Animal(string n) {
        name = n;
    }
    virtual void enjoy() {//增加了关键字virtual
        cout << "叫声" << endl;
    }
};
class Cat :public Animal {
public:
    string eyesColor;
    Cat(string n, string c) :Animal(n) {
        eyesColor = c;
    }
    void enjoy() {
        cout << "喵喵" << endl;
    }
    void enjoy(int i) {
        for (; i >= 1; i--) cout << "";
        cout << endl;
    }
};
class Dog :public Animal {
private:
    string furColor;
public:
    Dog(string n, string c) :Animal(n) {
        furColor = c;
    }
    void enjoy() {
        cout << "汪汪" << endl;
    }
};
void a() {
    Cat c("catname", "blue");
    Dog d("dogname", "black");
    Animal *pet;
    pet = &c;
    pet->enjoy();
    pet = &d;
    pet->enjoy();
}
int main() {
    a();
    getchar();
    return 0;
}
-------------------------------------------------------------------------
喵喵
汪汪
View Code

通过虚函数的使用,成功实现了通过基类的指针访问派生类对象的函数,两次调用实现了对不同对象的同名函数的调用,也就是对于同一个消息,不同的对象已不同的方式进行了响应,这就是运行时多态,需要动态联编来实现。

使用虚函数和指向基类指针的方法总结:

(1)在基类中声明虚函数,在派生类中重新定义同名的函数,并且函数类型和参数要和基类的虚函数完全一致。此时,派生类的同名函数会自动称为虚函数。

(2)定义基类对象的指针变量,并通过这个指针指向不同派生类对象就可以实现对不同对象的同名函数的调用。动态联编需要满足以下四个条件:1.有继承,2.有函数重载,3.基类中定义虚函数,4.基类指针指向派生类对象。

 

5.3 纯虚函数和抽象类

基类中不需要定义具体行为的函数可以定义为纯虚函数。反映一类事物共有特性的类可以定义为抽象类

5.3.1 纯虚函数

上文中提到基类Animal的函数enjoy()之所以奇怪就是因为这个函数其实根本没有必要去完成任何没有意义的操作,如果将其定义成纯虚函数,则具体的函数形式变为:

virtual void enjoy()=0;

对于纯虚函数声明的几点说明:

(1)纯虚函数只能在基类中声明;

(2)纯虚函数不可以被调用;

(3)纯虚函数不是函数体为空,而是没有函数体

(4)包含纯虚函数的派生类都应该重载纯虚函数,否则继承来的纯虚函数在派生类中仍是纯虚函数

5.3.2 抽象类

抽象类就是包含了一个或多个纯虚函数的类。如作为抽象类的Animal类是不能定义对象的,它只能作为专门的基类被派生,例如派生出类Cat和类Dog。

在使用抽象类时应注意以下几点:

(1)抽象类不能声明对象

(2)可以声明一个抽象类的指针和引用,以便于访问派生类的成员,实现运行时的多态

(3)抽象类中可以定义一个或多个纯虚函数,也可以定义有具体功能的函数并继承给派生类对象使用

(4)若抽象类的派生类中仍没有实现纯虚函数的功能,则派生类依然是抽象类。只有派生类实现了所有纯虚函数的功能才可以用来创建对象

问题:不能被实例化,还定义这个类做什么用???

抽象类/纯虚函数的实际用途,充当“接口规范” 

 

转载于:https://www.cnblogs.com/exciting/p/8586122.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值