C++17 --- 多态性、虚函数、多态的条件、联编(捆绑,绑定)
创始人
2024-02-14 22:42:16

一、多态性

1、多态

多态性是面向对象程序设计的关键技术之一。 若程序设计语言不支持多态性,不能称为面向对象的语言。

多态性(polymorphism) 多态性是考虑在不同层次的类中,以及在同一类中,同名的成员函数之间的关系问题。

函数的重载,运算符的重载,属于编译时的多态性。

以类的虚成员函数为基础的运行时的多态性,是面向对象程序设计的标志性特征。体现了 类推和比喻的思想方法。

多态的四种形态:

重载多态 -- 函数重载、运算符重载
包含多态 -- virtual 函数
参数多态 -- 模板
强制多态 -- 强制类型装换:static_cast , const_cast ,....

2、编译时的多态和运行时的多态

在这里插入图片描述

3、编译时多态,早期绑定

int max(int a,int b){ return a > b ? a : b;}char Max(char a,char b){ return a > b ? a : b;}double Max(double a,double b) { return a > b ? a : b; }int main()
{int x = Max(12, 23) ;char ch = Max('c', 'b');double dx = Max(12.23, 34.45);return 0 ;
}

机器代码,编译链接时的函数名称:
在这里插入图片描述

4、运行时的多态

1)运行时多态的设计思想:

对于相关的类型,确定它们之间的一些共同特征,(属性和方法) , 将共同特征被转移到基类中,然后在基类中,把这些共同的函数或方法声明为公有的虚函数接口。然后使用派生类继承基类,并且在派生类中重写这些虚函数,以完成具体的功能。这种设计使得共性很清楚,避免了代码重复,将来容易增强功能,并易于长期维护。

客户端的代码(操作函数)通过基类的弓|用或指针来指向这些派生类型对象,对虚函数的调用会自动绑定到派生类对象上重写的虚函数。

2)虚函数的定义:

虚函数是一个类的成员函数,定义格式如下:

virtual 返回类型 函数名(参数表) ;

关键字 virtual 指明该成员函数为虚函数。只能将类的成员函数定义为虚函数。当某一个类的成员函数被定义为虚函数,则由该类派生出来的所有派生类中,该函数始终保持虚函数的特征。

3)示例:运行时的多态性,晚绑定。

4)总结:

运行时的多态:共有继承 + 虚函数 + (指针或引用调用虚函数)。

二、虚函数

1、虚表

如果一个类包含了虚函数,不管有多少个虚函数,则增加了一个指针的大小。
有了一个虚指针 – VPtr ,vptr 指向一个虚表(vtable),虚表里面存储本类中虚函数的入口地址。

对于代码:

class A
{
public:virtual void fa(){cout << "A::fa" << endl;}virtual void fb(){cout << "A::fb" << endl;}virtual void fc(){cout << "A::fc" << endl;}
};void main()
{A a;
}

在这里插入图片描述
在这里插入图片描述

2、虚表的大小

不论有多少个虚函数,只要有,就多一个指针的大小。

示例代码:

class A
{
public:
};class B
{
public:void ffn(){}
};class C1
{
public:virtual void fa(){cout << "A::fa" << endl;}
};class C2
{
public:virtual void fa(){cout << "A::fa" << endl;}virtual void fb(){cout << "A::fb" << endl;}
};
class C3
{
public:virtual void fa(){cout << "A::fa" << endl;}virtual void fb(){cout << "A::fb" << endl;}virtual void fc(){cout << "A::fc" << endl;}
};void main()
{cout << sizeof(A) << endl;// 其为1cout << sizeof(B) << endl;// 也为1cout << sizeof(C1) << endl;//4cout << sizeof(C2) << endl;//4cout << sizeof(C3) << endl;//4
}

运行结果:

在这里插入图片描述

3、通过数组名加下标读取到虚表内的函数

	A a;typedef void (*FUN)(); //定义了一个函数指针FUN pf = NULL; //pf是函数指针类型,为指向返回值为void,里面没有参数的一类函数pf = (FUN)*( (int *)*(int *)( (int *)(&d) + n ) + m ); //pf指向类a内虚表所指向的第一个函数函数名,n、m为自己给的值,+n表示第几张虚表,+m表示虚表内的第几个函数pf();

4、 一个类的虚表

class A
{
public:virtual void fa(){cout << "A::fa" << endl;}virtual void fb(){cout << "A::fb" << endl;}virtual void fc(){cout << "A::fc" << endl;}
};void main()
{A a;typedef void (*FUN)(); //定义了一个函数指针FUN pf = NULL; //pf是函数指针类型,为指向返回值为void,里面没有参数的一类函数pf = (FUN)*(int *)(*(int *)(&a)); //pf指向类a内虚表所指向的第一个函数函数名pf();pf = (FUN)*((int *)(*(int *)(&a)) + 1);pf();pf = (FUN)*((int *)(*(int *)(&a)) + 2);pf();
}

运行结果:

在这里插入图片描述

在这里插入图片描述

5、一个类继承另一个类的虚表

同名同参的虚函数,会进行重写/覆盖

class A
{
public:virtual void fa(){cout << "A::fa" << endl;}virtual void ga(){cout << "A::ga" << endl;}virtual void ha(){cout << "A::ha" << endl;}
};class B:public A//B内有6个虚函数
{
public:virtual void fb(){cout << "B::fb" << endl;}virtual void gb(){cout << "B::gb" << endl;}virtual void hb(){cout << "B::hb" << endl;}
};class C:public A//C内有5个虚函数
{
public:virtual void fa(){cout << "C::fc" << endl;}//同名同参的虚函数,会进行重写/覆盖virtual void gc(){cout << "C::gc" << endl;}virtual void hc(){cout << "C::hc" << endl;}
};void main()
{cout << sizeof(A) << endl;//4cout << sizeof(B) << endl;//4cout << sizeof(C) << endl;//4cout<

运行结果:
在这里插入图片描述

三个类中虚表的内存布局:

在这里插入图片描述

6、一个类继承多个基类的虚函数

多继承 – 一个类继承多个类
基类有几个,就有几个虚表
将函数挂到第一个继承的类虚表内

对于多继承,在子类的对象中,每个父类都有自己的虚表,将最终与类的虚函数放在第一个父类的虚表中,这样做解决了不同的父类类型的指针指向比较清晰
如果在子类中重写了多个父类的同名同参虚函数,那么在虚表中同样做了修改

1)没有覆盖的情况

class A
{
public:virtual void fa(){cout << "A::fa" << endl;}virtual void ha(){cout << "A::ha" << endl;}
};class B
{
public:virtual void fb(){cout << "B::fb" << endl;}virtual void hb(){cout << "B::hb" << endl;}
};class C
{
public:virtual void fc(){cout << "C::fc" << endl;}virtual void hc(){cout << "C::hc" << endl;}
};class D:public A,public B,public C
{
public:virtual void fd(){cout << "D::fd" << endl;}virtual void hd(){cout << "D::hd" << endl;}
};int main()
{cout << sizeof(D) << endl ;//大小为12D d;typedef void(*FUN)();FUN pf = NULL;pf = (FUN)*(int *)( *(int *)(&d));pf();pf = (FUN)*((int *)( *(int *)(&d)) + 1 );pf();pf = (FUN)*((int *)( *(int *)(&d)) + 2 );pf();pf = (FUN)*((int *)( *(int *)(&d)) + 3 );pf();cout<

运行结果:

在这里插入图片描述

四个类的虚表内存布局

在这里插入图片描述

2)存在覆盖的情况

class A
{
public:virtual void ff(){cout << "A::fa" << endl;}//virtual void ha(){cout << "A::ha" << endl;}
};class B
{
public:virtual void ff(){cout << "B::fb" << endl;}//virtual void hb(){cout << "B::hb" << endl;}
};class C
{
public:virtual void ff(){cout << "C::fc" << endl;}//virtual void hc(){cout << "C::hc" << endl;}
};class D:public A,public B,public C
{
public:virtual void ff(){cout << "D::ff" << endl;}//virtual void hd(){cout << "D::hd" << endl;}
};int main()
{cout << sizeof(D) << endl ;//大小为12D d;typedef void(*FUN)();FUN pf = NULL;pf = (FUN)*(int *)( *(int *)(&d));//A::fapf();pf = (FUN)*((int *)( *(int *)(&d)) + 1 );pf();pf = (FUN)*((int *)( *(int *)(&d)) + 2 );pf();/*pf = (FUN)*((int *)( *(int *)(&d)) + 3 );pf();*/cout<

运行结果:
在这里插入图片描述

四个类的内存布局

在这里插入图片描述

三、多态的条件

1、覆盖(重写):

1、最少两个类,必须是父子关系;
2、同名同参虚函数,基类写了virtual,子类可以不写,他会将virtual继承下来

2、多态的条件:

1、满足覆盖
2、基类的指针或者引用指向基类对象或者派生类对象

四、联编(捆绑,绑定) – 函数调用 和 函数体联系的过程

联编是指计算机程序彼此关联的过程,是把一个标识符名和一个存储地址联系在一起的过程,也就是把函数的调用和函数的入口地址相结合的过程。

早捆绑/联编 – 在编译时
晚捆绑 – 在运行时

1、静态联编(static binding)早期绑定:

静态联编是指在编译和链接阶段,就将函数实现和函数调用关联起来。

C语言中,所有的联编都是静态联编,并且任何一种编译器都支持静态联编。
C++语言中,函数重载和函数模板也是静态联编。
C++语言中,使用对象名加点"."成员选择运算符,去调用对象虚函数,则被调用的虚函数是在编译和链接时确定。(称为静态联编)。

代码示例:

class A
{
public:virtual void fn(){ cout << "A::fn" << endl;}
};
class B:public A
{
public:virtual void fn(){ cout << "B::fn" << endl;}
};class C:public A
{
public:void fn(){ cout << "C::fn" << endl;}
};void test(A aa)
{aa.fn();
}void main()
{A a;B b;C c;a.fn();//编译时就知道这是a的fn(),为静态联编b.fn();//编译时就知道这是b的fn()c.fn();cout<

在这里插入图片描述

2、动态联编(dynamic binding)亦称滞后联编(late binding)或晚期绑定:

动态联编是指在程序执行的时候才将函数实现和函数调用关联起来。
C++语言中,使用类类型的引用或指针调用虚函数(成员选择符">"),则程序在运行时选择虚函数的过程,称为动态联编。

代码示例:
产生多态

class A
{
public:virtual void fn(){ cout << "A::fn" << endl;}
};
class B:public A
{
public:virtual void fn(){ cout << "B::fn" << endl;}
};class C:public A
{
public:void fn(){ cout << "C::fn" << endl;}
};void test2(A &aa)//引用,传本身
{aa.fn();
}void test3(A *p)//指针
{p->fn();
}void main()
{A a;B b;C c;a.fn();//编译时就知道这是a的fn(),为静态联编b.fn();//编译时就知道这是b的fn()c.fn();cout<

在这里插入图片描述

3、不加虚函数,不会产生多态

class A
{
public:void fn(){ cout << "A::fn" << endl;}
};
class B:public A
{
public:void fn(){ cout << "B::fn" << endl;}
};void test(A aa)
{aa.fn();
}void test2(A &aa)//引用,传本身
{aa.fn();
}void test3(A *p)//指针
{p->fn();
}void main()
{A a;B b;a.fn();//编译时就知道这是a的fn(),为静态联编b.fn();//编译时就知道这是b的fn()cout<

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...