c++多态

简介
多态可以分为静态和动态。
静态多态是指当调用名字相同的函数时,编译器在编译期就能根据函数签名的不同推导出所调用的函数。
注释:函数签名包括了函数名,参数列表,类名,命名空间,const关键字;
其实现也称为函数重载(overload),将类似功能的函数统一命名,增强代码的可读性。

动态多态一般体现在基类的指针可以指向一个派生类的对象,当该指针在运行期调用某一个虚函数时,根据所指向的对象类型的不同而调用相应的虚函数(《c++ primier》 pp.536 “被调用的函数是与绑定到指针或引用上的对象的动态类型相匹配的那一个”)。
当利用设计模式来解决问题时,会利用动态多态来实现,如达到“对扩展开放,对修改封闭”的目标。

下面将主要介绍动态多态的几个方面:虚函数表,虚函数表指针,动态绑定。

虚函数表指针和虚函数表
多态的实现是基于虚函数表指针和虚函数表。
举例来说:
基类有两个虚函数vf1和vf2,在编译时编译器会将这两个虚函数的地址放在基类的虚函数表(vtbl)内,供该类型的对象公用。然后编译器给基类增加一个成员变量vptr,即虚函数表指针,然后在基类的构造函数中增加一句赋值的命令,当实例化对象时,通过调用构造函数令vptr指向vtbl。
派生类由于继承自该基类,编译器判断基类有虚函数,那么派生类也会有一个vptr成员变量指向派生类自己的vtbl。如果派生类没有重写基类的虚函数,则其vtbl中的虚函数地址同基类的vtbl中虚函数的地址。
当派生类重写了某个虚函数时,派生类的vtbl中相应的函数地址就会更新为所重写的虚函数的地址。
另外,某个类的虚函数表是该类所共有的,因此会放在数据段,而类对象的虚函数表指针作为成员变量存在。
上述文字可以用下图表示(来自侯捷老师的视频):

image.png

另外,如果在派生类里面又定义了一个虚函数vf3,而vf3不在基类中,那么指向基类的指针并不会调用vf3,因为其不在基类的继承体系中。

动态绑定
要理解动态绑定,自然要理解静态绑定。
静态绑定是指编译器在编译阶段就能确定函数的地址,因此函数调用的汇编代码是call func_address(如图中的14行,CALL直接绑定了Base的print()函数)。

image.png

而由于要实现多态,那么在编译阶段,编译器是不知道所调用的函数的地址的,那么需要通过虚函数表指针和虚函数表来确定函数地址(如上图的21-27行,CALL的地址是经过偏移的),这个过程就是动态绑定。
动态绑定需要满足三个条件:

  1. 通过指针调用函数
    Base* p = new Derived();
  2. 指针指向的对象必须支持向上转型(up_casting),即指针是基类的类型,指向派生类的对象
  3. 所调用的函数是虚函数
    p->vfprint()
    此时,编译后的汇编代码不再指定具体的函数地址,而是先指向派生类的vptr,然后根据函数名来读取vtbl中对应的函数地址
    可以将虚函数调用过程用代码简化表示为
(*p->vptr[n])(p); 
(*(p->vptr)[n])(p);

即p指向了派生类对象的虚函数表,然后通过定位找到对应的虚函数。注意括号内的p相当于传入函数的this。
根据测试,虚函数的地址是有序的,可以从vfprint1()和vfprint2()的汇编代码看出,调用vfprint2()时地址add了8个字节(这里用的64位)


image.png

应用场景
假设我们要画不同的形状,可以将基类的draw设置为纯虚函数,然后派生类去各自实现。

class Shape
{
public:
    Shape() = default;
    virtual ~Shape() = default;

public:
    virtual void draw() = 0;
};

class Rectangle : public Shape
{
public:
    Rectangle() = default;
    virtual ~Rectangle() = default;

public:
    virtual void draw() override { std::cout << "draw a rectangle" << std::endl; };
};

class Circle : public Shape
{
public:
    Circle() = default;
    virtual ~Circle() = default;

public:
    virtual void draw() override { std::cout << "draw a circle" << std::endl; };
};

除了矩形和圆形之外,我们可能会在后续增加各种各样的形状,同时我们绝对不想改变画出所有图形的代码。那么在调用时可以写成下面,这样for循环中的代码是不会变的,只要将不同的图形加入到容器中就行。


int main()
{
    std::vector<Shape *> shapes;
    shapes.push_back(new Rectangle);
    shapes.push_back(new Circle);

    for (const auto &s : shapes)
    {
        s->draw();
        delete s;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容

  • 一、首先我们先来学习下:《Effect C++》学习------条款05:了解C++默默编写并调用哪些函数。如果你...
    Nrocinu阅读 180评论 0 0
  • 什么是多态性? 多态:相同对象收到不同消息或不同对象收到相同消息时产生不同的动作。C++支持两种多态性:编译时多态...
    showaichuan阅读 582评论 0 3
  • 14.1 多态的基本概念 在C++程序中,程序的每一个函数在内存中会被分配一段存储空间,而被分配的存储空间的起始地...
    飞扬code阅读 136评论 0 3
  • 多态原理 当类存在虚函数时,编译器会为该类维护一个表,这个表就是虚函数表(vtbl),里面存放了该类虚函数的函数指...
    第八区阅读 165评论 0 0
  • 什么是多态 多态是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数 多态分为两类:...
    杀破魂阅读 374评论 0 0