C++——函数重载,引用

news2024/11/13 22:20:57

 ✅<1>主页:我的代码爱吃辣
📃<2>知识讲解:C++
🔥<3>创作者:我的代码爱吃辣
☂️<4>开发环境:Visual Studio 2022
💬<5>前言:补充C语言语法的不足,以及C++是如何对C语言设计不合理的地方进行优化的,比如:作用域方面、IO方面、函数方面、指针方面、宏方面等。

目录

一.函数重载

(1)函数重载概念

1.参数类型不同

2.参数个数不同

3.参数类型顺序不同

(2)C++函数重载的原理

二.引用

(1)引用的概念

(2)引用特征

1.引用在定义时必须初始化

2.一个变量可以有多个引用

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

 (3)常引用

 (4) 使用场景

1.做参数

 2.作为返回值

 (5)传值、传引用效率比较 

 (6)引用和指针的区别


一.函数重载

在自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重
载了。
比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个
是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!”

(1)函数重载概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这
些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型
不同的问题。

1.参数类型不同

int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;
	return left + right;
}
int main()
{
	Add(10, 20);
	Add(10.1, 20.2);

	return 0;
}

 

2.参数个数不同

void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}

3.参数类型顺序不同

void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}

(2)C++函数重载的原理

我们知道C语言不支持函数重载,为什么C语言不支持函数重载呢,当有两个函数名相同的函数时,C语言就已经无法区分了,是因为C语言仅仅就是通过函数名来区分每个函数。但是在C++里面,C++通过对函数名配合参数进行修饰,就可以通过函数名以及函数参数特点,对每个函数进行区分。

通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修
饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。

如果两个函数函数名和参数都是一样的,仅仅是返回值不同是不构成重载的,因为调用时编译器没办法区分。

二.引用

(1)引用的概念

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

比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。

 类型& 引用变量名(对象名) = 引用实体;

看一段代码:

#include<iostream>
using namespace std;
int main()
{
	int a = 10;
	int& ra = a;
	std::cout << "a=" << a << endl;
	std::cout << "ra=" << ra << endl;
	ra = 20;
	std::cout << "a=" << a << endl;
	std::cout << "ra=" << ra << endl;

}

 

引用和对象使用同一块空间:

int main()
{
	int a = 10;
	int& ra = a;//<====定义引用类型
	printf("%p\n", &a);
	printf("%p\n", &ra);
}

 大家还记得这段代码吗:

void ListPushBank(ListNode**phead,int x)
{
	ListNode* newhead = (ListNode*)malloc(sizeof(ListNode));
	newhead->val = x;
	newhead->next = NULL;
	if (*phead == NULL)
	{
		*phead = newhead;
	}
	else
	{
		ListNode* Tail = *phead;
		while (Tail->next)
		{
			Tail = Tail->next;
		}
		Tail->next = newhead;
	}
}
int main()
{
	ListNode* head=NULL;
	ListPushBank(&head,100);
	ListPushBank(&head, 200); 
	return 0;
}

在PushBank时,如果我们遇到第一个结点时,就需要把第一个结点作为头结点,此时需要更改函数外面的 head 指针,就必须要 head 的地址,在函数里面就得使用二级指针。

现在我们有了引用就可以很方便的解决这个问题:

void ListPushBank(ListNode*& phead, int x)
{
	ListNode* newhead = (ListNode*)malloc(sizeof(ListNode));
	newhead->val = x;
	newhead->next = NULL;
	if (phead == NULL)
	{
		phead = newhead;
	}
	else
	{
		ListNode* Tail = phead;
		while (Tail->next)
		{
			Tail = Tail->next;
		}
		Tail->next = newhead;
	}
}
int main()
{
	ListNode* head = NULL;
	ListPushBank(head, 100);
	ListPushBank(head, 200);
	
	return 0;
}

(2)引用特征

引用有一些很重要的特征需要我们注意:

1.引用在定义时必须初始化

这里不难可能出语法直接时报错的。

2.一个变量可以有多个引用

nt main()
{
	int a = 10;
	int& ra = a;
	int& rb = a;
	int& rc = a;
	
	printf("ra=%d,rb=%d,rc=%d\n",ra,rb,rc);
	return 0;
}

一个变量可以有多个引用,并且多个引用都共用一个地址空间。

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

int main()
{
	int a = 10; 
	int b = 30;
	int& ra = a;
	ra = b;//仅仅只是把b的值赋值给了ra

	std::cout << &a << endl;
	std::cout << &ra << endl;
	std::cout << &b << endl;
	std::cout << "a=" << a << " b=" << b << endl;

	return 0;
}

 a与ra 仍然公用一块空间,ra = b,并没有改变ra的引用实体,实际上也改变不了ra的引用实体。

 (3)常引用

变量权限可缩小或平移,但不可放大

 场景一:

 因为常量只具有可读属性,如果引用成功的话,就可以通过引用来实现对变量的修改,变量权限就是放大了,这样也是说不通的。

 但是也不是没有办法使得引用的实体是常量。

 变量是只可读的,变量的引用对象也只能是可读的,这也就是变量权限平移.

 上述的结论,不仅针对引用,对指针也是有着同样的效果:

 场景二:

  1. 这里的报错,大家是不是认为是类型的原因导致的,但是往后看,加上一个const就没有报错了,而且仍然是int类型的引用。
  2. 首先来了解类型转的原理:类型转换是有一个中间变量的,而引用的对象的就是那个临时变量,因为临时变量具有常属性,所以加上一个 const 就可以消除报错。

(4) 使用场景

1.做参数

我们之前使用指针,实现函数来交换两个变量的值:

void Swap(int* pleft, int* pright)
{
	int tmp = *pleft;
	*pleft = *pright;
	*pright = tmp;
}

 原理我就不多解释了,想必大家都已经熟记于心了,今天我们用引用也可以实现:

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
int main()
{
	int a = 10; int b = 20;
	std::cout << "交换前a="<< a << ",b=" << b << endl;
	
	Swap(a, b);
	
	std::cout << "交换后a=" << a << ",b=" << b << endl;
	return 0;
}

 2.作为返回值

int& add(int a, int b)
{
	int sum = a + b;
	return sum;
}

int main()
{
	int& ret = add(1, 2);
    std::cout << ret << endl;
	add(3., 4);
	std::cout << ret << endl;
	return 0;
}

 问题来了,我们只对ret进行了一次赋值,但是两次输出却是两个值。这又是为什么呢?

 怎样可以返回引用呢?其实只要出了函数作用域,函数的返回值还在,就可以返回引用。

例如:static 修饰的变量存储在静态区,不会因为函数栈帧的销毁而销毁。



int& add(int a, int b)
{
    static int sum = a + b;
    return sum;
}

int main()
{
    int& ret = add(1, 2);
    std::cout << ret << endl;
    add(3., 4);
    std::cout << ret << endl;
    return 0;
}

 注意:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。

(5)传值、传引用效率比较 

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直
接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效
率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 1000000; ++i)
		TestFunc1(a);
	size_t end1 = clock();
	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 1000000; ++i)
		TestFunc2(a);
	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

int main()
{
	TestRefAndValue();
	return 0;
}

struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{
	// 以值作为函数的返回值类型
	size_t begin1 = clock();
	for (size_t i = 0; i < 1000000; ++i)
		TestFunc1();
	size_t end1 = clock();
	// 以引用作为函数的返回值类型
	size_t begin2 = clock();
	for (size_t i = 0; i < 1000000; ++i)
		TestFunc2();
	size_t end2 = clock();
	// 计算两个函数运算完成之后的时间
	cout << "TestFunc1 time:" << end1 - begin1 << endl;
	cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
int main()
{
	TestReturnByRefOrValue();
	return 0;
}

通过上述代码的比较,发现传值和传引用在作为传参以及返回值类型上效率相差很大。

 (6)引用和指针的区别

  • 在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
  • 在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

 引用和指针的不同点:

  1.  引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求。
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。
  4. 没有NULL引用,但有NULL指针。
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节)。
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。
  7. 有多级指针,但是没有多级引用。
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理。
  9. 引用比指针使用起来相对更安全。

 

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

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

相关文章

【Kubernetes】记录一次K8S容器内程序OOM排查过程:unable to create new native thread

文章目录项目背景问题初现问题排查问题定位问题解决项目背景 基于k8s的容器化kafka PaaS管理平台&#xff0c;业务团队申请kafka&#xff0c;通过一系列操作&#xff0c;封装crd&#xff0c;调用operator创建集群&#xff0c;当然还包括其他功能、topic管理、group管理、监控告…

年后上来面试了13家企业软件测试岗位,面试题整理

软件测试面试&#xff0c;800多道高频面试真题&#xff0c;随便刷。&#xff08;希望能帮助大家&#xff09;项目的测试流程 1. 拿到需求文档后&#xff0c;写测试用例 2. 审核测试用例 3. 等待开发包 4. 部署测试环境 5. 冒烟测试&#xff08;网页架构图&#xff09; 6.…

CSS中height:100vh和height:100%的区别是什么?

CSS中height:100vh和height:100%的区别 首先&#xff0c;我们得知道1vh它表示的是当前屏幕可见高度的1/100&#xff0c;而1%它表示的是父元素长或者宽的1%&#xff08;可以这么理解&#xff1f;&#xff09; 1、对于设置height:100%;有下面几种情况&#xff1a; &#xff08…

如何使用Maven快速构建JavaWeb项目?在idea中使用TomCat详细解读

文章目录1. 前言2. Web项目的结构3. 创建Maven Web项目4. 在IDEA中使用TomCat4.1 集成本地TomCat4.2 使用TomCat Maven插件5. 总结&#x1f4c2;橙子精品文章学习推荐1. 前言 前面在 Web 服务器 TomCat 快速入门一文中&#xff0c;我们介绍了 Web 服务器的基本概念以及 TomCat…

工业平板电脑实现工厂自动化设备无需手动连接

随着中国经济的快速发展和材料水平的不断提高&#xff0c;制造业的竞争日益激烈&#xff0c;市场竞静力逐渐转向质量、效率和价格服务&#xff0c;制造业企业面临更大的挑战&#xff0c;数据转型迫在眉睫。对工业平板电脑的需求也在增加&#xff0c;面向行业的工业平板电脑已成…

Java设计模式--工厂模式

目录 1.简单工厂模式 1.1类图 1.2 代码示例 2.工厂方法模式 2.1 类图 2.2 代码示例 3.抽象工厂模式 3.1 类图 3.2 代码示例 实际应用&#xff1a; 总结&#xff1a; 1.简单工厂模式 定义了一个创建对象的类&#xff0c;由这个类来封装实力化对象的行为。 1.1类图 1.…

《三体》中罗辑所说的定位行星的位置,是怎样实现的?

最近流浪地球2&#xff0c;三体电视剧火得一塌糊涂&#xff0c;《三体》中罗辑用咒语标记了三体星系位置&#xff0c;利用黑暗森林理论与三体人对峙长达两百年&#xff0c;那么这种定位技术在现实中是否存在呢&#xff1f;咒语标记三体星系位置这件事&#xff0c;听起来很玄乎但…

vite兼容chrome48的方法

chrome48不支持async await语法&#xff0c;但有些桌面客户端的内嵌浏览器就是chrome48,如下操作即可兼容 当前环境&#xff1a;2023-2-3使用npm create vitelatest创建 开始兼容操作 安装vite推荐的 vitejs/plugin-legacy 文档官网 https://github.com/vitejs/vite/tree/m…

【JavaEE】HTTP的方法、报头、状态码

✨哈喽&#xff0c;进来的小伙伴们&#xff0c;你们好耶&#xff01;✨ &#x1f6f0;️&#x1f6f0;️系列专栏:【JavaEE】 ✈️✈️本篇内容:http请求的方法、报头&#xff1b;状态码&#xff01; &#x1f680;&#x1f680;代码存放仓库gitee&#xff1a;JavaEE代码&#…

学习QCustomPlot【4】库官方examples之plots解读

文章目录一、前言二、案例解说0&#xff1a;Quadratic Demo【二次曲线demo】1、Simple Demo【简单demo】2、Sinc Scatter Demo【Sinc函数散点demo】3、Scatter Style Demo【散点样式demo】4、Line Style Demo【线型demo】5、Scatter Pixmap Demo【图标散点demo】6、Date Demo【…

RANSAC的实现与应用

一、前言RANSAC(Random Sample Consensus)算法并不陌生&#xff0c;在上一篇博客中&#xff08;基于SIFT的图像Matlab拼接教程&#xff09;也提到过&#xff0c;之前代码中也多次用过&#xff0c;其在直(曲)线拟合、特征匹配、过滤外点(Outlier)等领域有着重要的应用。RANSAC出…

线性代数之线性基

在谈论线性基之前,先介绍什么是基向量. 根据高中数学,一个二维直角平面坐标系中的所有向量都可以只用(0, 1)和(1, 0)合成.那么(0, 1)和(1, 0)就是基向量,所有基向量能合成的所有向量被称为基向量的张成空间. 在二维空间中,有没有其他的向量能作为基向量呢?答案是肯定的. 上图…

Oracle事務簡述

簡述本文主要介紹內容有事務的隔離級別&#xff0c;oracle支持的事務隔離級別&#xff0c;事務的提交與回滾&#xff0c;保存點內容事務的ACID特征介紹事務繞不過事務的ACID四個特征&#xff0c;這裡簡單回顧以下原子性&#xff08;Atomicity&#xff09;事務的執行要麼全部成功…

广义霍夫变换和模板匹配的不同

简述说到霍夫变换&#xff0c;做图像的知道经典霍夫变换最常用于检测规则曲线&#xff0c;如直线、圆、椭圆等。而广义霍夫变换是为了检出那些无法写出解析式的不规则形状&#xff0c;虽然在深度学习大行其道的时代&#xff0c;霍夫变换也还是有很多应用场景&#xff0c;另外广…

2023年黑马Java入门到精通教程--面向对象

推荐教程&#xff1a;java零基础入门到精通 面向对象编程的例子 设计类&#xff0c;创建对象并使用 1. 类和对象是什么&#xff1f; 类&#xff1a;是共同特征的描述(设计图)&#xff1b;对象&#xff1a;是真实存在的具体实例。 2. 如何设计类&#xff1f; 3. 如何创建对象…

CISP-PTE-Windows2003教程

为方便后续操作&#xff0c;建议和kali在同一网段。 获取到靶机IP后&#xff0c;扫描端口&#xff0c;1433是sqlserver的 测出用户名admin&#xff0c;但是密码爆破失败 扫描目录发现配置文件 配置文件中找到数据库的用户名和密码 使用Microsoft SQL Server Studio连接&#x…

MySQL从入门到精通(第0篇):全程有动画演示,适合入门学习

B站地址 文章目录一、MySQL的系统框架1. 连接池1.1 连接模块1.2 连接池2. SQL接口、SQL解析器、SQL优化器3. 存储引擎二、MySQL数据写入原理三、MySQL存储结构1. 使用InnoDB创建表2. 详述ibd文件中的存储结构2.1 页的数据连续存储2.2 行的结构2.3 区的结构2.4 组的结构2.5 段的…

剑指 Offer 33. 二叉搜索树的后序遍历序列

题目 输入一个整数数组&#xff0c;判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true&#xff0c;否则返回 false。假设输入的数组的任意两个数字都互不相同。 思路 二叉搜索树的特点是&#xff1a;左子树的值 < 根节点 < 右子树的值后序遍历的顺序是…

【逐步剖C】第三章-数组

一、一维数组 1. 一维数组的定义与使用 &#xff08;1&#xff09;数组的简单概念&#xff1a;一组具有相同类型的元素的集合 &#xff08;2&#xff09;数组的创建&#xff1a; 格式&#xff1a;类型名数组名[数组大小] 需要注意的是&#xff1a;对多数情况而言&#xff0c;…

# Vue中的Mixin混入

Vue中的Mixin混入 将组件的公共逻辑或者配置提取出来&#xff0c;哪个组件需要用到时&#xff0c;直接将提取的这部分混入到组件内部即可。这样既可以减少代码冗余度&#xff0c;也可以让后期维护起来更加容易。 1. 什么是Mixin&#xff1f; 混入 (mixin) 混入 (mixin) 将组…