zoukankan      html  css  js  c++  java
  • 多态性与虚函数

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

    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)若抽象类的派生类中仍没有实现纯虚函数的功能,则派生类依然是抽象类。只有派生类实现了所有纯虚函数的功能才可以用来创建对象

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

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

  • 相关阅读:
    CSS学习笔记之(1):文档流、块级元素、内联元素
    java nio纯理论
    CSS权重计算
    JS闭包(转载)
    [Journal]我是如何DIY博客的
    [CodeForces]Codeforces Round #428 (Div. 2)
    [Data Structure][线段树]BZOJ3211 花神游历各国
    [Journal]有一种感动叫ACM——记WJMZBMR在成都赛区开幕式上的讲话
    美团面试失败(Java开发)
    继承的初始化过程
  • 原文地址:https://www.cnblogs.com/exciting/p/8586122.html
Copyright © 2011-2022 走看看