【C->Cpp】由C迈向Cpp(3)


正文开始:

目录

(一)函数重载

 (1)函数重载

(2)函数重载实现原理

(二) 引用

(1)引用

(2)语法

         i ,别名:

        ii,传参:

        iii,返回值:

        iv,修改外部变量:

        v,避免空指针:


(一)函数重载

 (1)函数重载

        C语言不允许出现多个同名函数,而C++支持出现同名函数,这需要通过函数重载实现。 

        函数重载是指在一个类中,有多个同名的函数,但它们的参数列表不同。在C++中,函数重载可以通过函数名相同但参数列表不同来实现,参数列表不同可以包括参数类型、参数个数或者参数顺序等。当调用函数时,编译器会根据参数的类型或个数来自动选择合适的函数进行调用。

(2)函数重载实现原理

        在Linux下,C++函数重载的实现原理是使用了一种叫做"名称修饰"(Name Mangling)的技术。当C++源代码被编译成目标文件时,编译器会对函数的名称进行修饰,以区分不同的重载函数。

        名称修饰的过程是由编译器自动完成的,它将根据函数的参数类型、参数个数和参数顺序等信息生成一个唯一的符号名。这样,在目标文件中就能够通过不同的符号名来区分不同的重载函数。

(下面一linux的g++为例)

linux下的g++函数在修饰后变成:{   _Z + 函数名长度 + 函数名 + 类型首字母   }

//实例如下
Add(int,int)       ->   call     _Z3Addii
Add(double,double) ->   call     _Z3Adddd
//只要类型不同,类型顺序不同,类型个数不同,都可以让修饰后函数名不同,
//编译器可以区分不同的函数名,这样就构成了重载

        编译同一个文件,对于文件中的同一个函数,用gcc和g++编译出来的函数名称是不同的(符合上面规则):

用gcc编译的函数func1函数:

用g++编译的函数func1函数:

函数重载的作用主要有以下几点:

  1. 提高代码的复用性:通过函数重载,可以在一个类中定义多个功能相似的函数,避免了重复编写代码的问题。
  2. 提高程序的可读性:使用函数重载可以让程序更加直观清晰,减少了函数命名的复杂性。
  3. 增强了函数的灵活性:通过函数重载可以根据不同的参数选择不同的实现方式,从而提供了更多的选择。

        需要注意以下几点:

  • 重载函数的返回类型不可以作为重载的条件,只有参数列表不同才能作为重载的条件。
  • 重载函数的参数列表必须不同,参数个数不同或者参数类型不同都可以作为重载的条件。
  • 重载函数可以有不同的访问修饰符,比如一个是私有的,一个是公有的。

        需要注意的是,C++规定了可以函数重载,但是具体实现的名称修饰规则是由编译器决定的,不同的编译器可能会有不同的修饰规则。

(二) 引用

(1)引用

        引用不是新定义一个变量,而是给已存在的变量取了一个别名, 编译器不会为引用变量开辟内存空间,它和它引用的变量共同用一块内存空间

(2)语法

         i ,别名:

        引用提供了一个变量的别名,可以通过引用来访问已经存在的变量。这使得代码更加清晰和易读,同时也让函数传参更加方便,避免了拷贝大量数据的开销。

基本使用:

int i = 0;
int& j = i;//给i取别名j,j是i的别名

        j与i共用同一块内存地址;

        对i与对j的运算是等同的,j++后 i = 1 (其实就是i++ 后 i = 1); 

(引用类型必须和引用实体是同种类型的)

引用的特性:

        引用在定义时必须初始化;

        一个变量可以有多个引用;

 可以对别名取别名(套娃)对别名取别名,其实就是对原变量取别名,原变量与他的别名的地位是等同的(一个空间的名字可以是 “ i ”也可以是 “ j ” );

        引用一旦引用一个实体,不能再引用其他实体;

        引用需要与创建变量区分:

int i = 0; 
int j = i;//创建一个变量j初始化为0

        ii,传参:

        引用作为函数参数,可以将参数按引用传递,而不是按值传递。这样可以避免参数的拷贝,提高函数的执行效率,并且可以在函数内部修改参数的值(也就相当于传址),使得函数能够对传入的参数进行修改(同时也更好理解)。

 

//形参直接用实参的别名接收,便于直接对实参操作
void swap(int& r1,int& r2)
{int tem = r1;r1 = r2;r2 = tem;
}
int main()
{int a = 1,b = 2;swap(a,b);//不用再传地址,更好理解return 0;
}

        iii,返回值:

        函数可以返回引用类型,这样可以避免拷贝对象的开销,同时也方便链式调用和对象的赋值操作。通过返回引用,函数可以返回一个指向已存在的对象的引用,而不需要创建新的对象。

        对于n,是局部变量,存储在栈区,出count函数就会被销毁;

        编译器会先将n的值存储在寄存器中,当count函数栈帧销毁后,将寄存器的值赋给ret;(对于内置类型变量,确实是这样,但是对于自定义类型,拷贝需要调用拷贝构造,为了方便解释,按照内置类型)

int count()
{int n = 0;n++;return n;
}int main()
{int ret = count();return 0;
}

       


         对于变量n,是静态变量,存储在静态区,不会随着count函数的销毁而销毁;

        编译器仍然会先将n的值存储在寄存器中,当count函数栈帧销毁后,将寄存器的值赋给ret;

int count()
{static int n = 0;n++;return n;
}int main()
{int ret = count();return 0;
}

        为什么两种都需要寄存器的帮助?

        如果是返回引用,还需要寄存器的帮助吗?

​
int& count1()
{int n = 0;n++;return n;
}int& count2()
{static int n = 0;n++;return n;
}int main()
{int ret1 = count1();int ret2 = count2();return 0;
}​

        显然不需要,这样就减少了拷贝;

引用做返回值总结:

        第一种返回,直接返回变量的值,称为传值返回;传值返回需要一定的拷贝消耗;

        第二种返回,返回的是变量的引用(别名),称为传引用返回;传引用返回避免了拷贝消耗。

        (需要拷贝的数据类型不同,拷贝消耗不同。

        内置类型拷贝消耗较小,通常寄存器就可以充当新变量来辅助拷贝;

        自定义类型拷贝消耗较大,需要调用拷贝构造函数进行拷贝,为了避免这样的消耗,可以使用传引用返回)

        只要返回的变量不会随着函数栈帧的销毁而销毁,就可以使用拷贝构造。

        引用作返回值,同时也可以做可修改的左值,因为引用本质上就是变量的别名,变量当然可修改;

#define N 50//静态顺序表
struct AY
{int a[N];int size;    //大小int capacity;//容量
}//检查是否越界
int& posAt(AY& ay,int i)
{assert(i < N)return ay.a[i];
}int main()
{AY ay;for(int i = 0;i < N;i++){posAt(ay,i) = i * 10;//返回引用可作为可被修改的左值}
}


        iv,修改外部变量:

        引用可以作为函数的返回值,在函数内部对外部的变量进行修改。这种方式可以用于实现一些特殊的操作,如对象的赋值操作符重载等。通过引用,可以直接修改外部的变量,而不需要通过指针等方式。

        v,避免空指针:

        引用在定义时必须初始化,并且不能被修改指向其他对象,这样可以避免了指针的空引用问题。使用引用可以更加安全地操作对象,避免了空指针异常的发生。


 完~

未经作者同意禁止转载

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

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

相关文章

使用openeuler 22.03替代CentOS 7.9,建立虚拟机详细步骤

进入浏览器搜索网址下载openeuler 22.03镜像文件 https://mirrors.huaweicloud.com/openeuler/openEuler-22.03-LTS-SP3/ISO/x86_64/openEuler-22.03-LTS-SP3-x86_64-dvd.iso 打开VMware Workstation新建一个虚拟机&#xff1a; 自定义虚拟机位置 加入下载好的openeuler镜像文件…

嵌入式学习第十八天(目录IO)

目录IO: 1. mkdir int mkdir(const char *pathname, mode_t mode); 功能&#xff1a;创建目录文件 参数&#xff1a; pathname&#xff1a;文件路径 mode&#xff1a;文件的权限 rwx rwx rwx 111 111 111 0 7 7 7 r&#xff1a;目录中是否能够查看文件 w&#xff1a;目…

AMD FPGA设计优化宝典笔记(5)低频全局复位与高扇出

亚军老师的这本书《AMD FPGA设计优化宝典》&#xff0c;他主要讲了两个东西&#xff1a; 第一个东西是代码的良好风格&#xff1b; 第二个是设计收敛等的本质。 这个书的结构是一个总论&#xff0c;加上另外的9个优化&#xff0c;包含的有&#xff1a;时钟网络、组合逻辑、触发…

关于React中的状态和属性

在React中&#xff0c;状态&#xff08;State&#xff09;和属性&#xff08;Props&#xff09;是两个核心概念&#xff0c;用于管理组件的数据和传递信息。下面详细描述它们的区别&#xff1a; 状态&#xff08;State&#xff09;&#xff1a; 定义&#xff1a; 状态是组件内部…

面向对象 设计原则

0 引言 单一职责原则&#xff1a;类应该只有一个改变的理由&#xff1b; 开放-封闭原则&#xff1a;类应该对扩展开放&#xff0c;对修改关闭&#xff1b; 迪米特原则&#xff1a;只和朋友交谈&#xff1b; 里氏替换原则&#xff1a;子类可以扩展父类的功能&#xff0c;但不能…

5.14 BCC工具之vfsreadlat.py解读

一,工具简介 vfsreadlat.py工具用于跟踪VFS读取延迟分布情况,示例的运行结果是一个函数延迟分布直方图。 二,代码示例 #!/usr/bin/python# USAGE: vfsreadlat.py [interval [count]] # # The default interval is 5 seconds. A Ctrl-C will print the partially # gathere…

RocketMQ—RocketMQ消息重复消费问题

RocketMQ—RocketMQ消息重复消费问题 重复消费问题的描述 什么情况下会发生重复消费的问题&#xff1a; 生产者多次投递消息&#xff1a;如果生产者发送消息时&#xff0c;连接有延迟&#xff0c;MQ还没收到消息&#xff0c;生产者又发送了一次消息&#xff1b; 消费者方扩容…

「Python系列」Python函数

文章目录 一、Python函数函数的组成部分&#xff1a;调用函数&#xff1a;变量作用域&#xff1a;默认参数和可变参数&#xff1a; 二、匿名函数&#xff08;Lambda函数&#xff09;三、相关链接 一、Python函数 Python函数是组织代码的一种方式&#xff0c;它允许你定义可重用…

洛谷 8599.带分数

这道题其实是蓝桥杯的一道真题&#xff0c;其实这题看似很难解&#xff0c;但是如果稍加点化&#xff0c;其实很简单。 思路&#xff1a;因为我们需要选择1-9的数字来组成可以按照题目要求计算成目标数的形式&#xff0c;而且只有一次选用。那么&#xff0c;我们可以想&#x…

SEO优化对服务器有影响吗

SEO优化对服务器有影响吗 SEO优化也就是搜索引擎优化&#xff0c;简单的理解就是利用搜索引擎的规则来提高自己网站在搜索引擎上的自然排名。那么究竟服务器对于SEO优化是否有影响呢&#xff1f;SEO优化对服务器的选择有没有什么要求&#xff1f;服务器对SEO优化有什么影响&…

Docker命令实战

文章目录 一、Docker常用命令-图谱二、基础实战命令2.1、查找镜像2.2、启动容器2.3、修改容器内容2.3.1、进入容器内部修改2.3.2、挂载数据到外部修改 2.4、提交改变2.5、镜像传输--将镜像保存成压缩包2.6、两台主机间压缩文件的传输拷贝2.7、推送阿里云个人远程镜像仓库2.8、其…

【水文】计算并输出1到100之间所有质数的和

#include <stdio.h> #include <stdbool.h> // 函数声明 bool isPrime(int num); int main() { int sum 0; printf("1到100之间的质数有&#xff1a;\n"); for (int i 2; i < 100; i) { if (isPrime(i)) { printf("%…

CentOS7与Ubuntu防火墙管理命令操作总结

CentOS7与Ubuntu防火墙管理命令操作总结 CentOS7启动、停止和重启防火墙置默认区域查看防火墙状态和规则添加和删除规则重新加载和永久生效配置 Ubuntu防火墙管理操作安装和启用ufw验证ufw状态管理操作配置ufw规则删除和禁用规则 CentOS7 在CentOS7上&#xff0c;默认使用firew…

Nodejs 第三十九章(knex)

knex Knex是一个基于JavaScript的查询生成器&#xff0c;它允许你使用JavaScript代码来生成和执行SQL查询语句。它提供了一种简单和直观的方式来与关系型数据库进行交互&#xff0c;而无需直接编写SQL语句。你可以使用Knex定义表结构、执行查询、插入、更新和删除数据等操作。…

SQL110 插入记录(一)(插入和interval关键字的用法)

代码 insert into exam_record(uid,exam_id,start_time,submit_time,score) values(1001,9001,2021-09-01 22:11:12,2021-09-01 22:11:12interval 50 minute,90), (1002,9002,2021-09-04 07:01:02,null,null)知识点 interval关键字的用法 INTERVAL关键字一般使用格式为&#x…

Python爬虫之Splash负载均衡配置

爬虫专栏&#xff1a;http://t.csdnimg.cn/WfCSx Splash基础&#xff1a;Python爬虫之Splash详解-CSDN博客 用 Splash 做页面抓取时&#xff0c;如果爬取的量非常大&#xff0c;任务非常多&#xff0c;用一个 Splash 服务来处理的话&#xff0c;未免压力太大了&#xff0c;此…

【Linux 内核源码分析】内存映射(mmap)机制原理

内存映射(mmap)是 Linux 内核的一个重要机制&#xff0c;它为程序提供了一种将文件内容直接映射到进程虚拟地址空间的方式。同时内存映射也是虚拟内存管理和文件 IO 的重要组成部分。 在 Linux 中&#xff0c;虚拟内存管理是基于内存映射来实现的。在调用 mmap 函数时&#xf…

一篇博客教会你使用node多版本管理

文章目录 nvm 简介nvm 安装nvm 使用配置国内镜像显示可以安装的 nodejs安装 nodejs显示已安装的 nodejs切换 nodejs nvm 简介 nvm&#xff08;Node Version Manager&#xff09;是 node.js 的版本管理器&#xff0c;可以让我们轻松地在不同的 node.js 版本之间进行切换。 今天…

c语言(指针进阶)

指针 一.什么是字符指针二.使用指针数组模拟二维数组三.函数指针 一.什么是字符指针 字符指针&#xff1a;指向字符型数据的指针变量。每个字符串在内存中都占用一段连续的存储空间&#xff0c;并有唯一确定的首地址。即将字符串的首地址赋值给字符指针&#xff0c;可让字符指针…

vivado RAM HDL Coding Guidelines

从编码示例下载编码示例文件。 块RAM读/写同步模式 您可以配置块RAM资源&#xff0c;为提供以下同步模式给定的读/写端口&#xff1a; •先读取&#xff1a;在加载新内容之前先读取旧内容。 •先写&#xff1a;新内容立即可供阅读先写也是众所周知的如通读。 •无变化&…