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;发生了父类指针指向子类对象,隐藏由于是父类指针,所以调用了父类的实现,重写由于是子类对象,所以调用了子类实现。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【JAVA SE】初识JAVA

✨✨欢迎大家来到Celia的博客✨✨ &#x1f389;&#x1f389;创作不易&#xff0c;请点赞关注&#xff0c;多多支持哦&#x1f389;&#x1f389; 所属专栏&#xff1a;JAVA 个人主页&#xff1a;Celias blog~ 目录 ​编辑 一、关于JAVA 1.1 JAVA语言简介 1.2 语言优势 1…

Patch-Wise Graph Contrastive Learning for Image Translation

Patch-Wise Graph Contrastive Learning for Image Translation 图像翻译中的逐块图对比学习 Chanyong Jung1, Gihyun Kwon1, Jong Chul Ye1, 2 Chanyong Jung&#xff0c;Gihyun Kwon&#xff0c;Jong Chul Ye 1, 2 Abstract 摘要 Patch-Wise Graph Cont…

Windows快捷命令

Windows 操作系统提供了大量的快捷命令&#xff0c;用于快速访问系统设置和管理工具。这些命令在各个版本的 Windows 中基本都适用&#xff0c;可以帮助用户快速进入各类管理工具&#xff0c;方便系统的配置和管理。如果你需要使用这些工具&#xff0c;只需按 Win R 键&#x…

win11快速安装mysql数据库系统

win11快速安装mysql数据库系统 1、下载 1.1 打开官网 1.2 向下滚动页面 1.3 进入下载选项 1.4 下载8.0.4 LTS 1.5 开始下载 1.6 下载中 2、解压 大家注意&#xff0c;此时解压后目录是没有data目录的。 3、数据库初始化 3.1 管理员身份打开CMD 开始菜单上&#xff0c;输入…

【漏洞复现】Secnet-智能路由系统弱口令

0x01 产品简介 Secnet安网智能AC管理系统是广州安网通信技术有限公司(简称“安网通信”)的无线AP管理系统 0x02 漏洞描述 攻击者可直接利用弱口令登录系统 0x03 搜索语法 fofa: title"安网-智能路由系统" || title"智能路由系统" || title"安网科…

代码随想录算法训练营第三十一天|455.分发饼干,376. 摆动序列,53. 最大子序和

455.分发饼干 优先把小饼干分给胃口值小的&#xff0c;或者是把大饼干分给胃口大的。 376. 摆动序列 class Solution { public:int wiggleMaxLength(vector<int>& nums) {if (nums.size() < 1) return nums.size();int curDiff 0; // 当前一对差值int preDiff …

PostgreSQL扩展之PGroonga:多语言全文搜索

简介 PGroonga 是一个 PostgreSQL 扩展&#xff0c;它增加了基于 Groonga 的全文搜索索引方法。虽然原生的 PostgreSQL 支持全文索引&#xff0c;但它仅限于基于字母和数字的语言。PGroonga 提供了更广泛的字符支持&#xff0c;使其成为 PostgreSQL 支持的语言的超集&#xff…

Ubuntu20.04调试功能包的一些报错解决办法【更新中2024.05.14】

一、Could not find a package configuration file provided by “catkin_virtualenv” 解决办法&#xff1a; sudo apt install ros-noetic-catkin-virtualenv二、 ERROR: Could not find a version that satisfies the requirement pip-tools5.1.2 (from versions: none) …

将PDF转换成电子杂志,轻松打造畅销内容!

在数字化时代&#xff0c;将PDF转换成电子杂志是一种非常受欢迎的内容创作方式。这种方式不仅可以提高内容的传播效果&#xff0c;还可以为创作者带来更多的收益。那么&#xff0c;如何轻松地将PDF转换成电子杂志&#xff0c;打造畅销内容呢&#xff1f; 市面上有许多可以将PDF…

战网国际服加速器用哪个好 暴雪战网好用加速器介绍

战网国际版&#xff0c;又称Battle.net环球版&#xff0c;是暴雪娱乐操盘的全球性游戏互动平台&#xff0c;它跨越地理界限&#xff0c;服务于全球游戏爱好者。与地区限定版本相异&#xff0c;国际版赋予玩家自由进入暴雪旗下众多经典游戏的权利&#xff0c;无论身处何方&#…

【Linux 网络】网络基础(二)(应用层协议:HTTP、HTTPS)-- 详解

我们程序员写的一个个解决我们实际问题&#xff0c;满足我们日常需求的网络程序&#xff0c;都是在应用层。 前面写的套接字接口都是传输层经过对 UDP 和 TCP 数据发送能力的包装&#xff0c;以文件的形式呈现给我们&#xff0c;让我们可以进行应用层编程。换而言之&#xff0c…

Go微服务: 接入Prometheus性能监控平台与Grafana平台

接入Prometheus 在 go-micro 生成的模板中, 我们一如既往的完成基础工作之后 进入main.go工作的代码编写&#xff0c;main.go package mainimport ("fmt""log""strconv""github.com/go-micro/plugins/v4/registry/consul"opentracing…

【nfs服务部署服务端和客户端搭建】

原理 NFS&#xff08;Network File System&#xff09;是文件服务器之一。它的功能是可以通过网络&#xff0c;让不同的机器、不同的操作系统可以彼此共享数据文件。 NFS服务器可以让服务端的共享目录挂载到本地端的文件系统中&#xff0c;其他服务器如果想访问共享目录&#…

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数据帧的时…

基于死区补偿的永磁同步电动机矢量控制系统simulink仿真模型

整理了基于死区补偿的永磁同步电动机矢量控制系统simulink仿真&#xff0c;该模型使用线性死区补偿的PMSM矢量控制算法进行仿真&#xff0c;使用Foc电流双闭环 。 1.模块划分清晰&#xff0c;补偿前后仿真有对比&#xff0c;易于学习; 2.死区补偿算法的线性区区域可调; 3.自…

fyne更新GUI内容

fyne更新GUI内容 实现一个时钟界面&#xff0c;每秒钟更新一次。 package mainimport ("fyne.io/fyne/v2/app""fyne.io/fyne/v2/widget""time" )func updateTime(label *widget.Label) {formatted : time.Now().Format("2006-01-02 15:04…

Softing工业推出的edgeConnector将Allen-Bradley控制器集成到工业边缘应用中

2024年4月17日&#xff08;哈尔&#xff09;&#xff0c;Softing宣布扩展其基于Docker的edgeConnector产品系列&#xff0c;推出了新软件模块edgeConnector Allen Bradley PLC&#xff0c;可方便用户访问来自ControlLogix和CompactLogix控制器数据。 &#xff08;edgeConnector…

LSTM与GAN创新结合!模型性能起飞,准确率超98%

今天来聊一个深度学习领域非常具有创新性的研究方向&#xff1a;LSTM结合GAN。 LSTM擅长处理和记忆长期的时间依赖关系&#xff0c;而GAN可以学习复杂的数据分布并生成逼真的数据样本。通过充分结合两者的优势&#xff0c;我们可以增强模型对复杂数据的处理能力&#xff0c;提…

二叉树的常见操作

建立树 复制二叉树 计算深度 计算总结点数 计算叶子结点数