c++多态和虚函数

原创: 神秘编程 神秘编程 今天

问题抛出:为什么要使用多态?如果子类定义了与父类中原型相同的函数会发生什么?

多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。

那么C++是用什么实现多态的呢?

虚函数,抽象类,覆盖,模板(重载和多态无关)。

简单多态例子

#include<iostream>

using namespace std;

class Base

{

public:

Base(){}

virtual void foo()

{

cout<<"This is A."<<endl;

}

private:

int a;

int b;

};

class D: public Base

{

public:

D(){}

void foo()

{

cout<<"This is B."<<endl;

}

int x;

int y;

};

int main(int argc, char *argv[])

{

Base *a = new D();

a->foo();

if(a != NULL)

delete a;

return 0;

}

如果把virtual去掉,将显示:

This is A.

前面的多态通过使用虚函数virtual void foo()来实现。

到这里相信大家对多态有一丁点感觉了。

多态成立的个条件:

1:必须要有继承;

2:类中存在的成员函数是虚函数(virtual);

3:要有父类的指针或者引用指向子类对象;

4:子类一定要三同(返回值、函数名、参数列表);

5:二个类外;

(1)父类的成员函数返回值为 A*;子类的成员函数返回值为 B*,现在打破三同,还是覆盖。

(2)父类的析构函数一般都要加上(virtual),就可以利用多态性级联的调用析构函数,此时

先调用子类的,在调用父类的;(避免发生内存泄漏)。

内存模型

定义一个子类对象时,该子类就会有相应的内存情况,此时就是内存模型;

虚函数

虚函数是一个类的成员函数,其定义格式如下:virtual 返回值类型 函数名(参数表);

关键字virtual指明该成员函数为虚函数。

class Test()

{

public:

virtual void foo()

{

cout<<"This is A."<<endl;

}

}

虚函数剖析

(1)、当在类中出现一个(virtual),虚方法时,对象中第一个成员将是:_vptr;

此时只有一个成员,应该为4B,但是其因为有虚函数的存在,内部的第一个成员就一定为虚表指针;

指针32位下为4字节,所以此时一共为8字节;(不管内部有多少个虚函数,但是虚表指针只有一个);

(2)、虚函数将在继承体系中,一直为虚(覆盖时); 此时的virtual可以省略不写;

(3)、多态:就是对虚方法的重写;

(4)、虚表:装虚函数的表,其本质是地址的覆盖;

多态的原理

没有覆盖的虚表

如果子类有一个方法与父类的虚方法三同,此时覆盖;

但是通过父类的指针/引用永远只能访问父类对象的部分;

或取得虚表中的函数

Base b;

cout<<&b<<endl;   //对象的地址

printf("%p\n", *(int *)(&b));  //虚表中虚表指针的值(指针4字节),所以转换整形指针,也就是虚表指针的地址;

((Fun)*((int*)*(int*)(&b) + 0))();  //Fun是函数指针,将获得虚表中的第一个函数;

((Fun)*((int*)*(int*)(&b) + 1))();  //Fun是函数指针,将获得虚表中的第二个函数;

((Fun)*((int*)*(int*)(&b) + 2))();  //Fun是函数指针,将获得虚表中的第三个函数;

多继承中虚表

父类均为虚函数,子类中也有虚函数,且没有进行覆盖,则将子类的放到第一个虚表的最后,其余的父类虚表就不用放了;因为,

就是放了,通过父类的指针/引用也访问不了,浪费内存空间;要是有覆盖的,则每个虚表都得画出;其余情况类似分析就行。

纯虚函数与抽象类

#include<iostream>

using namespace std;

class Test{

public:

   virtual void fun() = 0;  //这种形式就是存虚函数,赋值为0;

   virtual void fun1() = 0;

   virtual void fun2() = 0;

   virtual void fun3() = 0;

};

int main(void){

   return 0;

}

单继承下的虚函数表

//单继承下虚函数表:是如何组织的

class A{

public:

virtual void func(){

cout << "A::func" << endl;

}

virtual void funcA(){

cout << "A::funcA" << endl;

}

};

class B:public A{

public:

virtual void func(){

cout << "B::func" << endl;

}

virtual void funcB(){

cout << "B::funcB" << endl;

}

};

class C:public A{

public:

virtual void func(){

cout << "C::func" << endl;

}

virtual void funcC(){

cout << "C::funcC" << endl;

}

};

typedef void (*FUNC)();

int main()

{

A a;

B b;

C c;

cout << "A::虚表:" << endl;

((FUNC)(*(int *)(*(int*)(&a))))();

((FUNC)(*((int*)(*(int*)(&a)) + 1)))();

cout << "-------------------------------------" << endl;

cout << "B::虚表:" << endl;

((FUNC)(*(int *)(*(int*)(&b))))();

((FUNC)(*((int*)(*(int*)(&b)) + 1)))();

((FUNC)(*((int*)(*(int*)(&b)) + 2)))();

cout << "-------------------------------------" << endl;

cout << "C::虚表:" << endl;

((FUNC)(*(int *)(*(int*)(&c))))();

((FUNC)(*((int*)(*(int*)(&c)) + 1)))();

((FUNC)(*((int*)(*(int*)(&c)) + 2)))();

system("pause");

return 0;

}

单继承情况下:(虚函数的存放情况----->存放地址)

1:先基类的虚函数;

2:再派生类的虚函数;

3:存在覆盖的话,派生类的虚函数占据了覆盖的基类的虚函数位置;

typedef void(*FUNC)();

class A{

public:

virtual void func(){

cout << "A::func" << endl;

}

virtual void funcA(){

cout << "A::funcA" << endl;

}

private:

int a;

};

class B{

public:

virtual void func(){

cout << "B::func" << endl;

}

virtual void funcB(){

cout << "B::funcB" << endl;

}

private:

int b;

};

class C :public A, public B{

public:

virtual void func(){

cout << "C::func" << endl;

}

virtual void funcC(){

cout << "C::funcC" << endl;

}

private:

int c;

};typedef void(*FUNC)();

class A{

public:

virtual void func(){

cout << "A::func" << endl;

}

virtual void funcA(){

cout << "A::funcA" << endl;

}

private:

int a;

};

class B{

public:

virtual void func(){

cout << "B::func" << endl;

}

virtual void funcB(){

cout << "B::funcB" << endl;

}

private:

int b;

};

class C :public A, public B{

public:

virtual void func(){

cout << "C::func" << endl;

}

virtual void funcC(){

cout << "C::funcC" << endl;

}

private:

int c;

};

多继承条件下的虚函数表

//多继承条件下的虚函数表

void test()

{

C c;

cout << "多继承条件下的虚函数表:" << endl;

cout << "------------------------" << endl;

((FUNC)(*((int*)(*(int *)(&c)))))();

((FUNC)(*((int*)(*(int*)(&c)) + 1)))();

((FUNC)(*((int*)(*(int*)(&c)) + 2)))();

cout << "------------------------" << endl;

((FUNC)(*(int*)(*((int*)(&c) + 2))))();

((FUNC)(*((int*)(*((int*)(&c) + 2)) + 1)))();

}

多继承下的虚函数表------->有几个基类就有几张虚函数表-----派生类的虚函数存放在声明的基类的虚函数表中。

1:首先要声明基类的虚函数表

该类的成员变量(int a);

2:然后再声明基类虚的函数表

该类的成员变量(int b);

3:派生类中与基类造成覆盖的-->分别占据相应的位置-->C::func分别位于二个基类的虚函数形成覆盖。

4:派生类中未覆盖的,放在首先声明的基类的虚函数后面----->C::funC。

多继承条件下,基类指针指向派生类后,基类指针所能访问的函数

//多继承条件下,基类指针指向派生类后,基类指针所能访问的函数

void test1()

{

C c;

A *pa = &c;

B *pb = &c;

C *pc = &c;

cout << "基类指针pa所能调用的函数:" << endl;

pa->func();

pa->funcA();

//pa->funcB();error:提示类A没有成员funcB、funcC  -->受到类型的限制

//pa->funcC();error

cout << "基类指针pb所能调用的函数:" << endl;

pb->func();

pb->funcB();

//pb->funcA();error

//pb->funcC();error

cout << "派生类指针pc所能调用的函数:" << endl;

pc->func();

pc->funcA();

pc->funcB();

pc->funcC();

}

多继承条件下,基类指针指向派生类对象后,基类指针之间强制类型转化之后,所能访问的函数

void test2()

{

C c;

A *pa = &c;

B *pb = &c;

C *pc = &c;

pa = reinterpret_cast<A *>(pb);

pa->func();

pa->funcA();

//pb = reinterpret_cast<B *>(pa);

//pb->func();

//pb->funcB();

//pa = reinterpret_cast<A *>(pc);

//pa->func();

//pa->funcA();

}


如果喜欢,欢迎关注微信公众号:神秘编程   一起讨论学习

专注Linux C/C++和算法知识学习分享总结

                   欢迎关注交流共同进步

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

推荐阅读更多精彩内容