C++中的重载,隐藏,覆盖,虚函数,多态浅析
直到今日,才发现自己对重载的认识长时间以来都是错误的。幸亏现在得以纠正,真的是恐怖万分,雷人至极。一直以来,我认为重载可以发生在基类和派生类之间,例如:
class A {
public:
void test(int);
};
class B : public A {
public:
void test(int, int);
}; void main()
{
B b; b.test(); //错误,应该b.A::test(5);
}
我一直认为当类B把类A中的test函数继承之后,在类B中,类A的test函数和类B自己定义的test函数是重载关系(因为我觉得这两个函数靠形参个数区分开来了),进而,我就认为第14行会调用类A的test函数。非常雷人。现在把重载和隐藏的注意事项总结出来,供理解有误的人们参考:
重载:
在一个类内,如果存在若干个同名函数,而且这些函数之间可以用形参个数或者形参类型区分开来的时候(注意不能靠函数返回类型区分),这几个函数就互为重载函数。这时,当你通过类对象调用这几个函数时,编译器就可以通过你传递的实参个数或者类型,去匹配相应的函数,而不会发生歧义。这也就是重载函数的作用所在(让你可以使用若干个同名函数)。需要注意的是:
1.重载绝对不会发生在基类和派生类之间,如上例所示。当基类和派生类中存在同名函数时,无论同名函数的形参个数或者类型是否相同,派生类中的同名函数都会将基类中的同名函数隐藏掉,因此它们是隐藏关系,而不是重载关系。关于隐藏,后边会提到。如此以来,上例的14行在编译时就会报错,提示类B中没有test(int)类型的函数。
2.在同一个类中,重载函数之间必须依靠形参个数或者形参类型来进行区分,不能依靠返回类型。也就是说,如果同一个类中的两个同名函数形参个数和类型完全相同,但是返回值类型不同,这时候编译就会报错,因为当你通过类对象调用该同名函数时,编译器会出现二义性,不知道该选择哪个函数。记着,重载必须靠形参来区分。
3.在同一个类中,虚函数和虚函数,虚函数和普通函数之间也可以重载,规则完全同上。虚函数下边会提到。
隐藏:
隐藏只能出现在基类和派生类之间,而不能发生在同一个类内(比如上述2中,只会引起编译器出现二义性)。当基类和派生类中存在同名函数时,无论同名函数的形参个数或者类型是否相同,派生类中的同名函数都会将基类中的同名函数隐藏掉,而不会是重载关系。这时,当你通过派生类对象调用该同名函数时,只能访问派生类的该函数,如果硬要访问基类的该函数,则需要在函数名前加上类作用域,如上边代码所示。
覆盖:
覆盖也只能出现在基类和派生类之间,当派生类和基类中的存在同名函数,且参数个数和参数类型完全相同,并且基类中的该函数有virtual修饰(派生类中的该函数可有可无),则派生类的该函数覆盖掉基类的该函数。该性质用来实现多态。
虚函数:
在一个类中,用virtual关键字声明的函数都是虚函数。虚函数存在的唯一目的,就是为了实现多态(动态绑定/运行时绑定)。关于多态,后面会提到。虚函数只有在基类和派生类之间才能发挥虚特性(也就是说才能发挥虚函数的真正的目的)。在同一个类中,所有虚函数就和普通函数是一样,使用同样的重载规则(重载的第3点中提到过)。因此在同一个类中可以把虚函数看作普通函数来使用(因为其虚特性发挥不出来),使用方法和注意事项与普通函数一模一样。
多态:
多态是面向对象思想的精髓所在。说白了,就是通过基类指针或引用调用一个成员函数时,直到运行阶段在才能决定该成员函数是哪个派生类中定义的成员函数。有点抽象吧?没事,先看一段代码吧。
class A {
public:
virtual void test(int);
}; class B : public A {
public:
void test(int);
}; class C : public A {
public:
void test(int);
}; void main()
{
A *a0;
A &a1 = b;
A &a2 = c;
B b;
C c; a0 = &b;
a0.test(); //调用类B的test函数 a0 = &c;
a0.test(); //调用类C的test函数 a1.test(); //调用类B的test函数
a2.test(); //调用类C的test函数
}
我们先说什么是多态吧,随后再讲产生多态的条件。第18行,在main函数中定义了一个指向类A类型的指针变量a0,第21和22行分别定义了派生类B和C的对象b,c。第24行,将对象b的指针赋给a0,第25行a0.test将调用类B的test函数;第27行,将对象c的指针赋给a0,第28行a0.test将调用类C的test函数。这就是多态,有感觉了吗?说白了,就是当基类指针变量指向了哪个派生类对象,就可以调用哪个派生类对象的方法。类似的,引用也可以实现多态,第19-20,30-31行所展示的。
下面总结下实现多态的条件:
哪些成员函数想要以多态的形式来执行,那么这些函数必须:
1.在基类中将这些成员函数声明为虚函数,并实现(必须要实现)。
2.在派生类中也声明这些成员函数并实现(必须实现),基类和派生类的这些函数必须同名,而且其形参个数和类型,返回值类型必须与基类中的这些函数完全相同。此时,派生类中这些函数无论是否用virtual来声明,都会被自动虚化。
3.将派生类对象赋给基类的指针变量或者引用。至此,多态实现,可用基类指针或引用调用派生类的方法(符合多态条件的方法,而不是普通方法)。
实现多态的这三个条件必须完全满足,虚函数的虚特性才能发挥出来,也才能实现多态。缺少任何一个条件,虚函数的虚特性都会被打破,无法实现多态。虚特性被打破的虚函数和普通函数是一样的,因此说虚函数的唯一用途就是实现多态。
下面举一些不是多态的例子:
a.基类中声明为虚函数,派生类中也声明为虚函数,并且也同名。但是派生类中该函数的形参类型或者形参个数和基类中的不相同。此时,多态不满足,派生类和基类的这两个虚函数仅仅是隐藏关系,没有虚特性。
b.基类中声明为虚函数,派生类中也声明了一个同名函数,但没有使用virtual,并且形参类型或者形参个数和基类不相同。这时候基类中的虚函数也丢失虚特性,派生类的该函数不会被虚化,当然也就够不成多态,这两个函数也仅仅是隐藏关系。
c.基类中的函数不是虚函数,派生类中声明为虚函数,它们同名,这时也够不成多态,派生类的虚函数没有虚特性,它们也是隐藏关系。
d.基类和派生类的两个函数同名,都是虚函数,形参的个数和类型也都相同,但是返回值类型不同,这时编译会报错,因为两个虚函数在隐藏时,返回值类型发生了冲突,因此隐藏发生错误。注意,如果这两个函数不是虚函数,这不会报错,隐藏会成功;同时,如果派生类中是虚函数,基类中不是虚函数,也不过报错,隐藏也是成功的。这也说明,虚化并隐藏时,返回值类型一定要保持相同。
C++中的重载,隐藏,覆盖,虚函数,多态浅析的更多相关文章
- C++中的重载隐藏覆盖&&JAVA中的重载覆盖&&多态
class 类继承默认是private, struct 默认继承是public C++中的隐藏: 只要派生类中出现和基类一样的函数名,基类中的函数就会被派生类中的函数给隐藏(如果派生类和基类中的函数名 ...
- C++中的重载、覆盖、隐藏
前几天面试时被问及C++中的覆盖.隐藏,概念基本答不上来,只答了怎么用指针实现多态,也还有遗漏.最终不欢而散.回来后在网上查找学习了一番,做了这个总结.其中部分文字借用了别人的博客,望不要见怪.引用的 ...
- Delphi中静态方法重载还是覆盖的讨论
Delphi中静态方法重载还是覆盖的讨论 新人学习Delphi的时候,容易搞不懂的一个问题,当子类方法和基类方法同名,并且参数也一样的时候,叫做什么呢?是覆盖,还是重载呢? 答案是隐藏父类方法. 一般 ...
- VCL控件组件大都应该重载TWinControl的虚函数WndProc来进行处理窗口消息的工作
TWinControl的构造函数中会调用MakeObjectInstance并且传递MainWndProc作为窗口消息处理函数,而MainWndProc则会调用虚函数WndProc来处理窗口消息.留个 ...
- cc38b_demo_C++_异常_(2)txwtech在异常中使用虚函数-多态
//cc38b_demo,21days_C++_异常_(2)txwtech20200121在异常中使用虚函数-多态 //--异常层次结构//*异常的类-创建自己的异常类//*异常派生-就是继承//*异 ...
- c++ 虚函数多态、纯虚函数、虚函数表指针、虚基类表指针详解
静态多态.动态多态 静态多态:程序在编译阶段就可以确定调用哪个函数.这种情况叫做静态多态.比如重载,编译器根据传递给函数的参数和函数名决定具体要使用哪一个函数.动态多态:在运行期间才可以确定最终调用的 ...
- C++中的虚继承 & 重载隐藏覆盖的讨论
虚继承这个东西用的真不多.估计也就是面试的时候会用到吧.. 可以看这篇文章:<关于C++中的虚拟继承的一些总结> 虚拟基类是为解决多重继承而出现的. 如:类D继承自类B1.B2,而类B1. ...
- c++中的重载、覆盖和隐藏
1 重载发生在同一个类内部. 同一个类内部,具有相同的函数名,但是参数列表不同,那么就是重载.因为c++编译器编译时,将函数名和函数列表一起对函数进行了重命名. 2 覆盖和隐藏发生在子类和父类之间. ...
- C++中为什么构造函数不能是虚函数,析构函数是虚函数
一, 什么是虚函数? 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语 ...
- C++中overload(重载),override(覆盖),overwrite(重写/覆写)的区别
#include <cstdio> #include <cstdlib> class Base { public: #pragma region MyRegion1 //函数重 ...
随机推荐
- linux shell 命令学习(1) du- estimate file space usage
du - estimate file space usage , 计算文件的磁盘大小 语法格式: du [OPTION] ... [FILE] 描述: 汇总每个文件的磁盘大小, 递归汇总目录的大小, ...
- 爬虫Larbin解析(一)——Larbin配置与使用
介绍 功能:网络爬虫 开发语言:c++ 开发者:Sébastien Ailleret(法国) 特点:只抓取网页,高效(一个简单的larbin的爬虫可以每天获取500万的网页) 安装 安装平台:Ubun ...
- 开发板挂载nfs服务器错误解析
输入mount -t nfs 192.168.1.110:/home/work /mnt,这时可能会出现 mount.nfs:access denied by server while mountin ...
- DNSget Ip
var address = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(a => a.AddressFamily ...
- CFF前端沙龙总结
一. -OOCSS + Sass ——大漠 1. OOCSS 结构<=>皮肤 分离 容器<=>内容 分离 2. Sass 工具.处理器 SCSS(CSS风格)<=> ...
- servlet基础讲解
基本知识一.Web结构1.两种应用程序 ①桌面应用程序:QQ.CS.MyEclipse.Office.DW.360.浏览器等必须下载.安装.桌面快捷方式.注册表信息.操作系统后台服务.占用操作系统端口 ...
- freemarker中判断对象是否为空
<#if xxx?exists> 或则 <#if xxx??>两个问号??最简单方便
- JAVA将Excel中的报表导出为图片格式(三)换一种实现
上一篇介绍了使用Java的Robot机器人实现截图,然后将剪贴板上的数据流生成PNG图片 但是经过博主的不断测试,在完全依赖远程桌面的没有终端显示器的服务器上 使用截图方式是不可行的,因为一旦使用了远 ...
- createElement 创建DOM元素
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...
- 用户输入 i. 检测常用手势(一)
参考: http://blog.csdn.net/qq418716640/article/details/8508973http://www.cnblogs.com/mengdd/p/3335508. ...