多态,虚函数,纯虚函数,虚函数表

多态性:一个接口,多种方法.程序在运行时才确定调用的函数,是 oop 的核心概念.

  • 多态性通过虚函数来实现,子类可以重新定义父类(重写:override).

重写有两种,一种是重写虚函数(体现多态),另一种就是重写成员函数(并没有体现)

和重写相对的另一个概念是重载(overloading),指的是多个重名的函数他们的参数列表不同(个数,类型),编译器通过函数的调用参数列表来决定调用的

  • 多态和非多态的本质区别在于:早绑定和晚绑定.函数调用的地址是编译期间就能确定的,那就是非多态;需要在运行的时候才确定,那就是多态
  • 面向对象三大特性
    1. 封装:模块化代码,实现代码重用
    2. 继承扩展已经实现的代码,实现代码重用
    3. 多态: 实现接口重用,同一个接口可以自适应到各自对象的实现方法上面去.
  • 实现多态的方法
    1. 声明基类指针,指向一个子类对象
    2. 调用虚函数
    3. 如果没有多态性,则调用的函数将一直是基类的相应函数,即,函数调用的地址是固定的,不能实现一个接口,多种方法.
#include<iostream>  
using namespace std;  
  
class A  
{  
public:  
    void foo()  
    {  
        printf("1\n");  
    }  
    virtual void fun()  
    {  
        printf("2\n");  
    }  
};  
class B : public A  
{  
public:  
    void foo()  
    {  
        printf("3\n");  
    }  
    void fun()  
    {  
        printf("4\n");  
    }  
};  
int main(void)  
{  
    A a;  
    B b;  
    A *p = &a;  
    p->foo();  
    p->fun();  
    p = &b;  
    p->foo();  
    p->fun();  
    return 0;  
}  

输出为

1 2 1 4

对于输出1 2是没有问题的,在第三和第四个输出的时候,因为基类指针指向了子类,而foo()函数没有被虚拟化,所以,这是一个早绑定,只能调用基类的同名函数,fun()是一个基类中的虚函数,所以可以被晚绑定,调用子类中的函数,从而实现了一个借口,多个函数的多态性.

  • 小结
    1. 如果有 virtual 才有多态(覆盖基类函数)
    2. 没有多态,按照原类型调用(隐藏)

纯虚函数:在基类中定义的虚函数,没有定义,派生类需要定义自己的实现方法.

virtual void function() = 0;

派生类中必须进行重写以实现多态性. 含有纯虚函数的类成为抽象类,不能生成对象.

  • 编译多态性: 通过重载实现
  • 运行多态性: 通过虚函数实现(覆盖)

虚函数表(vtable): 虚函数是通过虚函数表来实现的,这个表主要是一个类虚函数的地址表,这张表解决了继承和多态的问题,保证了真实反映和使用实际的函数.这个表在一个对象实例的最前面,我们可以通过变量虚函数表的函数指针,调用相应的函数.

class Base {
     public:
            virtual void f() { cout << "Base::f" << endl; }

            virtual void g() { cout << "Base::g" << endl; }

            virtual void h() { cout << "Base::h" << endl; }
};
Base b;

这个对象实例b的结构如下:

图片.png

  1. 一对一继承(子类没有覆盖重写虚函数,这样在实际中没有意义,仅为对比)
    如果子类也有自己的虚函数,并继承父类的虚函数表,则其虚函数表的结构为:


    图片.png

    1)虚函数按照声明顺序放在表中
    2)父类的虚函数放在子类的前面

  2. 一对一继承(有虚函数被覆盖)
    父子两个对象的表分别如下:


    图片.png

则最后子类的虚函数表将为:


图片.png

1)覆盖的虚函数,父类位置被子类顶替
2)其余顺序不变
因此,如果有

Base *b = new Derive();
b->f();

该父类对象指向的地址是子类的地址,对象b将调用覆盖后的 Derive::f().
这就是多态实现的原理.

  1. 多重继承(无覆盖)
    如果子类对父类的虚函数没有覆盖:


    图片.png

    那么子类实例中的虚函数表是这样子的:


    图片.png

    1)每个父类有自己的虚表
    2)子类的虚函数在第一个父类之后
    3)父类顺序是按照声明顺序来的

    当每次设计到这些虚函数的时候,需要对应到相应的虚函数表(根据基类的定义),比如:

Base2 *ptr = new d();
ptr->f()  //调用第二个表的第一个函数(Base2 的 f())
  1. 多重继承(有虚函数覆盖)


    图片.png

    所有相应的同名虚函数都要被覆盖:


    图片.png

安全性

  • 子类中没有重载的虚函数虽然出现在第一个虚函数表中,但是相应的父类指针并不能访问这个函数.程序将报错.但是仍然可以通过地址访问的方式调用.
  • 对于父类中private的方法,如果是虚函数,并被子类通过共有继承,那么这个函数将出现在子类的虚函数表中,并可以通过地址访问的方式被调用,这是很危险的.
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 多态(1)静态多态与动态多态 什么是多态 从字面上理解就是多种形态的意思。而多态一词最初源自希腊语,其含义便是“多...
    kingZXY2009阅读 2,299评论 0 2
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • 继承和多态 1. 继承的优缺点 优点:(1)子类可以灵活地改变父类中的已有方法;(2)能够最大限度的实现代码重用。...
    MinoyJet阅读 620评论 0 0
  • 前言:一般查询可以通过find方法,但如果是比较复杂的查询或者数据统计的话,find可能就无能为力了,这时也许你需...
    n_ll阅读 1,907评论 0 3
  • 若雪儿是城市里的普通女孩儿,快读高一了。五官端正、皮肤白白净净的,尤其是眼睛,纯洁美丽,长得很漂亮。是那种让...
    星雪蔷薇阅读 201评论 0 1