C++ 中重写重载和隐藏的区别

重写(override)、重载(overload)和隐藏(overwrite)在C++中是3个完全不同的概念。我们这里对其进行详细的说明

1、重写(override)是指派生类覆盖了基类的虚函数,这里的覆盖必须满足有相同的函数签名和返回类型,也就是说有相同的函数名、形参列表以及返回类型。

2、重载(overload)是指C++允许在同一作用域中声明几个功能类似的同名函数,这些函数的函数名相同,但是函数签名不同,也就是说有不同的形参。

3、隐藏(overwrite)是指基类成员函数,无论它是否为虚函数,当派生类出现同名函数时,如果派生类函数签名不同于基类函数,则基类函数会被隐藏。如果派生类函数签名与基类函数相同,则需要确定基类函数是否为虚函数,如果是虚函数,则这里的概念就是重写;否则基类函数也会被隐藏。另外,如果还想使用基类函数,可以使用using关键字将其引入派生类。

一、重写

函数重写的基本原则:在基类中,通过使用关键字 virtual 来声明一个虚函数,派生类可以通过重新定义基类中的虚函数来实现函数重写。

例如:

class Father {
public:virtual void func() {cout << "Father" << endl;}
};class Child: public Father {
public:void func() {cout << "Child" << endl;}
};int main() {Father f;Child c;f.func();c.func();
}

以上代码实现了子类重写父类中的函数。所以父类子类调用同一个函数会有不同的实现。仿真如下:

image-20240515164906765

1.1、重写引发的问题

重写虚函数很容易出现错误,原因是C++语法对重写的要求很高,稍不注意就会无法重写基类虚函数。且这些错误不易被发现,编译器可能也不会提示:

class Base {
public:virtual void some_func() {}virtual void foo(int x) {}virtual void bar() const {}void baz() {}
};class Derived : public Base {
public:virtual void sone_func() {}virtual void foo(int &x) {}virtual void bar() {}virtual void baz() {}
};

Derived的4个函数都没有触发重写操作。第一个派生类虚函数sone_func的函数名与基类虚函数some_func不同,所以它不是重写。第二个派生类虚函数foo(int &x)的形参列表与基类虚函数foo(int x)不同,所以同样不是重写。第三个派生类虚函数bar()相对于基类虚函数少了常量属性,所以不是重写。最后的基类成员函数baz根本不是虚函数,所以派生类的baz函数也不是重写。

1.2、使用override说明符

重写容易出错,尤其继承关系非常复杂的时候。所以C++11标准提供了一个非常实用的override说明符,明确告诉编译器这个虚函数需要覆盖基类的虚函数,一旦编译器发现该虚函数不符合重写规则,就会给出错误提示。

class Derived : public Base {
public:virtual void sone_func() override {}virtual void foo(int &x) override {}virtual void bar() override {}virtual void baz() override {}
};

如果没有override说明符,则修改基类虚函数将面临很大的风险,因为编译器不会给出错误提示,我们只能靠测试来检查问题所在。

1.3、使用final说明符

可以为基类声明纯虚函数来迫使派生类继承并且重写这个纯虚函数。但是一直以来,C++标准并没有提供一种方法来阻止派生类去继承基类的虚函数。C++11标准引入final说明符解决了上述问题,它告诉编译器该虚函数不能被派生类重写。final说明符也需要声明在虚函数的尾部。

class Father {
public:virtual void func() final {cout << "Father" << endl;}
};class Child: public Father {
public:// 报错,不能重写final修饰的函数void func() {cout << "Child" << endl;}
};

最后要说明的是,final说明符不仅能声明虚函数,还可以声明类。如果在类定义的时候声明了final,那么这个类将不能作为基类被其他类继承

class Base final {
public:virtual void foo(int x) {}
};// 报错,不能继承final修饰的类
class Derived : public Base {
public:void foo(int x) {};
};

1.4、override和final的特别之处

在C++11标准中,override和final并没有被作为保留的关键字,其中override只有在虚函数尾部才有意义,而final只有在虚函数尾部以及类声明的时候才有意义,因此以下代码仍然可以编译通过:

void override() {}
void final() {}

二、重载

函数重载的条件:

1、参数个数不同

2、参数类型不同

3、参数顺序不同

void fun(int i) {cout << "打印整数: " << i << endl;
}
void fun(int i, int j) {cout << "打印两个整数: " << i << " 和 " << j << endl;
}
void fun(float f) {cout << "打印浮点数: " << f <<endl;
}fun(4);				// 调用第一个 fun 函数  
fun(2, 3);			// 调用第二个 fun 函数
fun(1.5f);          // 调用第三个 fun 函数  

**注意:**返回值不同不是函数重载的判断标准

void fun(int i) {cout << "打印整数: " << i << endl;
}int fun(int i) {cout << "打印整数: " << i << endl;return 0;
}fun(4);             // 报错,并不知道调用哪个函数

2.1、函数重载的底层原理

为什么C++支持函数重载而C不支持?C语言中同名函数编译完还是同名的,两个重名函数的地址都是有效值,所以在重定位的时候就会产生冲突和歧义。而C++会对函数名进行修饰,例如:

void f(int a, double b) { printf("%d %lld", a, b) }

函数名 f 会被修正为 _Z1fid,Linux下的命名规则为

函数名被修饰为:_Z + 函数名长度 + 函数名 + 各个形参类型首字母的小写

这也就解释了为什么函数重载和返回值无关,为什么和参数个数,类型,顺序不同就可以重载,因为他们修饰完后的函数名就是不同的。

三、隐藏

隐藏指在某些情况下,派生类中的函数屏蔽了基类中的同名函数:

1、两个函数参数相同,但是基类不是虚函数。和重写的区别在于基类函数是否是虚函数

2、两个函数参数列表不同,无论基类函数是否虚函数,基类函数都将被覆盖。和重载的区别在于两个函数不在同一个类中

下面举例说明:

class Base {
public:void funA(){cout<<"funA()"<<endl;}virtual void funB(){cout<<"funB()"<<endl;} 
};class Heri:public Base {
public:// 隐藏,基类中同名函数不是虚函数void funA(){cout<<"funA():Heri"<<endl;}// 隐藏,参数列表不同,无论基类是否是虚函数,基类函数都将被覆盖void funA(int a){cout<<"funA(int a):heri"<<a<<endl;}// 重写,基类是虚函数void funB(){cout<<"funB():heri"<<endl;}
};

隐藏使用的时候记住一句,派生类的指针或引用,对象调用子类和父类同名的函数,父类的同名函数被子类隐藏,调用的是子类的函数

看下面代码:

class Base {
public:void fun1() { cout<<"base:fun1()"<<endl; fun(); }virtual void fun() { cout<<"base:fun()"<<endl; }
};class Deriverd:public Base {
public:virtual void fun1() { cout<<"deriverd:fun1()"<<endl; }void fun() { cout<<"deriverd:fun()"<<endl; }
};int main() {Base *pb = new Deriverd;pb->fun1();return 0;
}

输出结果为:

image-20240515173641206

main函数中创建了父类的指针指向了子类的对象,然后通过父类的指针调用具有隐藏关系的fun1()函数,我们会以为pb->fun1()调用的是子类的函数fun1(),实际并不是,隐藏关系的函数,谁调用就用谁的函数,按照正常的函数调用使用便可得正确的结果,这里是父类指针调用,就用父类的函数fun1()。这就是隐藏和重写的区别。

四、重写与隐藏的区别

看下面的代码:

class Base {
public:virtual void foo(int x) { cout << "Base: " << x << endl;}void foo(int x, int y) { cout << "Base: " << x << ' ' << y << endl; }
};// 报错,不能继承final修饰的类
class Derived : public Base {
public:void foo(int x) { cout << x << endl; };void foo(int x, int y) {cout << x << ' ' << y << endl; }
};
int main() {Base *pb = new Derived;pb->foo(1);pb->foo(1, 2);return 0;
}

image-20240515174346499

其中 foo(int x, int y) 函数发生了隐藏,void foo(int x)函数发生了重写, Base *pb = new Derived;发生了父类指针指向子类对象,隐藏由于是父类指针,所以调用了父类的实现,重写由于是子类对象,所以调用了子类实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/705588.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GD32F103RCT6/GD32F303RCT6-UCOSIII底层移植(1)工程建立

本文章基于兆易创新GD32 MCU所提供的2.2.4版本库函数开发 后续项目主要在下面该专栏中发布&#xff1a; 手把手教你嵌入式国产化_不及你的温柔的博客-CSDN博客 感兴趣的点个关注收藏一下吧! 电机驱动开发可以跳转&#xff1a; 手把手教你嵌入式国产化-实战项目-无刷电机驱动&am…

OpenAI推出旗舰AI模型GPT-4o并免费开放

&#x1f989; AI新闻 &#x1f680; OpenAI推出旗舰AI模型GPT-4o并免费开放 摘要&#xff1a; OpenAI 未来的产品将以免费为优先&#xff0c;以让更多人使用为目标。OpenAI 发布了桌面版本的程序和更新后的 UI&#xff0c;更加简单自然。推出了新一代大模型 GPT-4o&#xf…

Qt自定义QpushButton分别在c++/python中实现

//.h文件#ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QPainter> #include<QMouseEvent> #include<QPropertyAnimation> #include<QResizeEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; }class Widget : public QWi…

React Native 开发心得分享

有一段时间没更新了&#xff0c;花了点时间研究了下 React Native&#xff08;后续用 RN 简称&#xff09;&#xff0c;同时也用该技术作为我的毕设项目(一个校园社交应用&#xff0c;仿小红书)&#xff0c;经过了这段时间的疯狂折腾&#xff0c;对 RN 生态有了一定的了解&…

C语言/数据结构——栈的实现

一.前言 今天我们讲解新的领域——栈。 二.正文 1.栈 1.1栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其允许在固定的一段进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#…

【漏洞复现】Secnet-智能路由系统 actpt_5g.data信息泄露

0x01 产品简介 Secnet安网智能AC管理系统是广州安网通信技术有限公司(简称“安网通信”)的无线AP管理系统 0x02 漏洞描述 Secnet智能路由系统 acipt 5g.data 接口存在信息泄露漏洞&#xff0c;未经身份验证的远程攻击者可以利用此漏洞获取系统账户名密码等重要凭据&#xff…

OFDM 802.11a的FPGA实现(十六)长训练序列:LTS(含Matlab和verilog代码)

目录 1.前言2.原理3.Matlab生成长训练序列4.硬件实现5.ModelSim仿真6.和Matlab仿真结果对比 原文链接&#xff08;相关文章合集&#xff09;&#xff1a; OFDM 802.11a的xilinx FPGA实现 1.前言 在之前已经完成了data域数据的处理&#xff0c;在构建整个802.11a OFDM数据帧的时…

如何启用WooCommerce商城快捷结帐:3 种简单方法

使用WooCommerce商城快捷结帐可帮助您提高商店的转化率。 70%的顾客同意在线商店的快速结账流程会鼓励他们完成购买。 在结账过程中您让购物者完成的步骤越多&#xff0c;他们完成该流程的可能性就越小。 解决方案是什么&#xff1f; 通过跳过默认的WooCommerce商城购物车页…

从RTTR谈Reflection机制

虽然C11引入了RTTI、Metaprogramming 等技术&#xff0c;但C在Reflection编程方面依旧功能有限。在社区上&#xff0c;RTTR则提供了一套C编写的反射库&#xff0c;补充了C在Reflection方面的缺陷。 零、环境 操作系统Windows 11Visual StudioVisual Studio Community 2022 CMa…

Excel提取某一列的唯一值

点击【筛选】&#xff08;【高级筛选】&#xff09;&#xff0c;参数里&#xff1a; 列表区域&#xff1a;为需要选择唯一值的那一列复制到&#xff1a;生成唯一值的目标区域 据说新版本的excel有了unique()函数&#xff0c;可以很快捷的选择某一列的唯一值&#xff0c;但是博…

知识图谱必须要图数据库嘛?

在 ZH上又看到一个问题&#xff0c;觉得挺有意思&#xff0c;小聊一二。 “知识图谱必须要图数据库吗&#xff1f;” ——使用非关系型数据库&#xff0c;关系型数据库&#xff0c;在计算图的一些特征上&#xff0c;通过优化算法是否能达到使用图数据库接近的计算速度呢&#x…