多态&重载、隐藏(重定义)、重写(覆盖)

多态:

多态按字面的意思就是多种形态,相同的方法调用,但是有不同的实现方式。多态性可以简单地概括为“一个接口,多种方法”。C++有两种多态形式:

  • 静态多态
  • 动态多态

静态多态(静态绑定):也称为编译期间的多态,编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。

静态多态有两种实现方式:

函数重载:包括普通函数的重载和成员函数的重载

函数模板的使用

动态多态(动态绑定):即运行时的多态,在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。

1.通过基类类型的引用或者指针调用虚函数

2.必须是虚函数(派生类一定要重写基类中的虚函数)

首先搞清楚这个对象的类型:

  • 静态类型:对象声明时的类型,编译时确定
  • 动态类型:目前所指对象的类型,运行时确定

多态&重载、隐藏(重定义)、重写(覆盖)_第1张图片

重载:

重载定义:在同一作用域中,同名函数的形式参数(参数个数、类型或者顺序)不同时,构成函数重载。

重载函数与返回值,参数名无关

  1. 类的静态成员函数与普通成员函数可以形成重载
  2. 函数重载发生在同一作用域,如类成员函数之间的重载、全局函数之间的重载
  3. 函数返回值类型与构成重载无任何关系

多态&重载、隐藏(重定义)、重写(覆盖)_第2张图片

重载为什么改变参数就可以实现调用不同的函数?

因为C++在编译的时候会对函数进行重命名,保证函数的唯一性,重载函数的参数不同,就会被命名成不用的函数名。

构造函数可以被重载么?析构函数呢?

构造函数可以被重载,因为构造函数有多个,而且可以带参数。

析构函数不能被重载,因为析构函数只有一个,不能带参数。

为什么C语言中没有重载呢?

编译器在编译期间创建的一个字符串,用来指明函数的定义或原型。C和C++程序的函数在内部使用不同的名字修饰方式。

C编译器的函数名修饰规则:

对于__stdcall调用约定,编译器和链接器会在输出函数名前加上一个下划线前缀,函数名后面加上一个“@”符号和其参数的字节数,例如_functionname@number。__cdecl调用约定仅在输出函数名前加上一个下划线前缀,例如_functionname。__fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,例如@functionname@number。

C++编译器的函数名修饰规则:

C++的函数名修饰规则有些复杂,但是信息更充分,通过分析修饰名不仅能够知道函数的调用方式,返回值类型,参数个数甚至参数类型。不管 __cdecl,__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”开始,后面紧跟函数的名字,再后面是参数表的开始标识和 按照参数类型代号拼出的参数表。对于__stdcall方式,参数表的开始标识是“@@YG”,对于__cdecl方式则是“@@YA”,对于__fastcall方式则是“@@YI”。参数表的拼写代号如下所示:

X–void

D–char

E–unsigned char

F–short

H–int

I–unsigned int

J–long

K–unsigned long(DWORD)

M–float

N–double

_N–bool

U–struct

….

所以,C++编译器能识别函数特征标的不同,从而实现重载。

隐藏重(定义):

隐藏定义:指不同作用域中定义的同名函数构成隐藏(不要求函数返回值和函数参数类型相同)。比如派生类成员函数隐藏与其同名的基类成员函数、类成员函数隐藏全局外部函数。

隐藏的实质:在函数查找时,名字查找先于类型检查。如果派生类中成员和基类中的成员同名,就隐藏掉。编译器首先在相应作用域中查找函数,如果找到名字一样的则停止查找。

隐藏是一个静态概念,它代表了标识符之间的一种屏蔽现象,而覆盖则是为了实现动态联编,是一个动态概念

重写(覆盖):

重写的定义派生类中与基类同返回值类型、同名和同参数的虚函数重定义,构成虚函数覆盖,也叫虚函数重写。需要注意的是,这里有一个特殊情况,即协变返回类型

协变定义:如果虚函数返回指针或者引用时(不包括value语义),子类中重写的函数返回的指针或者引用是父类中被重写函数所返回指针或引用的子类型。

多态&重载、隐藏(重定义)、重写(覆盖)_第3张图片

多态&重载、隐藏(重定义)、重写(覆盖)_第4张图片

为什么要重写?????

前几天在网上看到这样的一个问题:

多态现在一般的用法,就是拿一个父类的指针去调用子类中被重写的方法。但我搞不懂为什么要那么做,我们直接在子类中写一个同名的成员函数,从而隐藏父类的函数不就行了么?

然后有人这样回答:

将父类比喻为电脑的外设接口,子类比喻为外设,现在我有移动硬盘、U盘以及MP3,它们3个都是可以作为存储但是也各不相同。如果我在写驱动的时候,我用个父类表示外设接口,然后在子类中重写父类那个读取设备的虚函数,那这样电脑的外设接口只需要一个。但如果我不是这样做,而是用每个子类表示一个外设接口,那么我的电脑就必须有3个接口分别来读取移动硬盘、U盘以及MP3。若以后我还有SD卡读卡器,那我岂不是要将电脑拆了,焊个SD卡读卡器的接口上去?

如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。(重载在同一作用域,隐藏在不同作用域,重写要求返回类型,参数列表全部一样)

如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。(不同作用域、没有virtual)

你可能感兴趣的:(C++那些事,c++)