首页 > 编程语言 >【C++】踏上C++学习之旅(三):“我“ 与 “引用“ 的浪漫邂逅

【C++】踏上C++学习之旅(三):“我“ 与 “引用“ 的浪漫邂逅

时间:2024-10-22 22:16:25浏览次数:3  
标签:邂逅 函数 之旅 int C++ 编译器 引用 返回值 指针

文章目录

前言

本文会着重的讲解"引用"的各项用法以及使用时需要注意的一些规则,另外这部分是面试官比较喜欢与指针一起作为问题来提问我们的,所以我在文章的末尾,给大家也准备好了答案!

话不多说,让我们从现在开始与"引用"进行一场浪漫的邂逅的吧!!!

哈哈哈

1. "引用"的概念

引用不是一种新的数据类型,而是在C++中给已存在变量起一个别名。编译器不会给引用变量开辟内存空间,它和它引用的变量共用同一块空间

举个例子,在《水浒传》中,李逵在家中被宋江叫做"铁牛",在江湖上人称"黑旋风"。那么,我们说"铁牛"和"黑旋风"都是在说李逵这个人,所以说"铁牛"和"黑旋风"就是别名。 在代码的世界里,相信大家已经对别名有所使用,就是typedef这个关键字通常被有做对结构体起别名。在C++中,引用是对变量起别名!

讲解完引用是什么之后,那我们就来看看,引用是如何在代码中表示的。

1.1 "引用"的语法

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

下面我来写一段代码,带着大家感受一下"引用"的魅力:

#include<iostream>
using namespace std;

int main()
{
	int a = 10;
	int& ra = a;  //类型& 引用变量名 = 引用实体;

	cout << "a = " << a << endl;
	cout << "ra = "  << ra << endl;

	return 0;
}

引用演示

大家在注意引用的定义时,引用变量与引用实体必须得是相同的数据类型。否则,程序会报错的!

2. "引用"的特性

1. 引用在定义时必须初始化;
2. 一个变量可以有多个引用;
3. 一旦有个实体被引用,那么这个引用变量就不能再引用其它实体。

int main()
{
	int a = 10;
	//int&ra; //该条语句在编译时会报错
	int& ra = a;
	int& rra = a;
	printf("%p  %p  %p",&a,&ra,&rra);
}

错误示范

tupian
以上的代码案例就能很好的体现出引用的特性!

哈哈哈


3. "引用"的使用场景

光讲引用的定义和特性,相信这一定不能让大家认识到"引用"有多强大,有多舒服。那么,接下来,我结合C语言的代码场景来对比在C++下,引用的强大之处。

3.1 "引用"做参数

"引用"做参数,主要是针对输出型参数。

什么是输出型参数?
输出型参数:通过形参的改变能影响实参的改变。 这类的形参,我们就把它称为输出型参数。当然与之对应的还有输入型参数,这个参数想必大家肯定用的不少,==输入型参数:形参的改变不会影响到实参。==这类的形参,我们称它为输入型参数。

好处:
1. 针对输出型参数
2. 减少拷贝,提高效率(特别是大对象/深拷贝对象)

为了让大家更好的感受到"引用"的"爽",我将用多个代码在C语言下和C++下做比较:

//场景一:交换两个数(用C语言)
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//交换两个数(用C++的"引用")
void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}
struct Stack{
	int* a;
	int top;
	int capacity;
};

//场景二:给栈初始化(用C语言)
void StackInit(struct Stack* pst,int STDefault = 4)
{
	pst->a = (int*)malloc(sizeof(int)*STDefault);
	if(pst->a == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	pst->top = 0;
	pst->capacity = STDefault;
}

//给栈初始化(用C++的"引用")
void StackInit(struct Stack& st,int STDefault = 4)
{
	st.a = (int*)malloc(sizeof(int)*STDefault);
	if(st.a == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	st.top = 0;
	st.capacity = STDefault;
}

int main()
{
	struct Stack st;
	//用C语言的版本
	StackInit(&st);
	//用C++的版本
	StackInit(st);
}

大家可以仔细对比一下,是C语言指针的写法好用,还是C++的"引用"好用。

StackInit函数struct Stack& st这个形参就相当于输出型参数,当然这个想象在Swap函数更加明显。

好了,"引用"作为形参的第一个好处我理解了,那第二个好处又怎么解读呢?

我说引用作为参数,可以减少拷贝,提高效率 ,这个点就体现在函数栈帧的创建和销毁中。如果对这方面不了解的读者,可以看一下往期我写的文章:【C语言】函数栈帧的创建和销毁(启航——迎接崭新的自己)

回到主线上,我们在调用一个函数时,会在栈区给函数的调用开辟一块空间,这块空间就是函数的栈帧,编译器开会就会往栈中压入以西寄存器之类的东西。重点来了,随后,它就会把我们的形参从右往左依次压入栈中,在这个过程中是通过寄存器将形参先拷贝下来,而这段拷贝是要花时间的。 而我们使用"引用"的话,就可以掠过拷贝的过程,这将这个变量给放到栈中,减少了拷贝的花销。

大家可以拷贝一下程序,在你自己的电脑检测一下:

#include<iostream>
#include<time.h>
using namespace std;

//减少拷贝,提高效率
struct test //创建一个大对象
{
	int a[10000];
};

test a;
void Func1(test a) {};
void Func2(test& a) {};

int main()
{
	int begin1 = clock();
	for (int i = 1; i <= 10000; i++)
	{
		Func1(a);
	}
	int end1 = clock();

	int begin2 = clock();
	for (int i = 1; i <= 10000; i++)
	{
		Func2(a);
	}
	int end2 = clock();


	cout << "time void Func1(test a) : " << end1 - begin1 << endl;
	cout << "time void Func2(test& a) : " << end2 - begin2 << endl;

	return 0;
}

结果
可以看到,引用确实是提高了程序的效率!

当然,引用不仅在参数中能大放异彩,它在做函数返回值时也同样优秀。

3. 2 "引用"做返回值

好处:
1. 减少拷贝,提高效率(特别是大对象/深拷贝对象)
2. 修改返回值 + 获取返回值

请大家看下面的代码:

#include <time.h>
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 < 100000; ++i)
 		TestFunc1();
 	size_t end1 = clock();
 	// 以引用作为函数的返回值类型
 	size_t begin2 = clock();
 	for (size_t i = 0; i < 100000; ++i)
 		TestFunc2();
 	size_t end2 = clock();
 	// 计算两个函数运算完成之后的时间
 	cout << "TestFunc1 time:" << end1 - begin1 << endl;
 	cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

结果

3.2.1 "引用"做返回值时需要注意的点

请大家想看看下面这个代码的结果:

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}

引用做返回值
可以看到引用作为返回值时,及其容易出错,那错误的原因就是"非法访问"。

有的读者此时就会说,程序也没有崩溃,何来的"非法访问"一说?
有时候并不是编译器不报错就不代表你这个代码没有问题,就像数组越界一样。

标签:邂逅,函数,之旅,int,C++,编译器,引用,返回值,指针
From: https://blog.csdn.net/tianxiawushanu/article/details/143167131

相关文章

  • 【C++-NOIP篇-4】 [NOIP2007 普及组] 纪念品分组
    文章目录[NOIP2007普及组]纪念品分组题目背景题目描述输入格式输出格式样例#1样例输入#1样例输出#1提示题目思路完整Code[NOIP2007普及组]纪念品分组题目背景NOIP2007普及组T2题目描述元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参......
  • 施磊c++基础8
    STL内容学习简介C++STL:standardtemplatelibarayvector容器底层数据结构:动态开辟的数组。每次以空间大的二倍扩容增加vec.push_back(20);末尾添加元素20—O(1)vec.insert(it,20);在it迭代器指向的位置插入元素20—O(n)删除vec.pop_back;末尾删除元素----......
  • 施磊c++基础7
    C++的四种类型转换c语言中提供的类型强转inta=(int)b;c++提供:const_cast:去掉常量属性的一个类型转换 int*p1=(int*)&a; int*p2=const_cast<int*>(&a);这两句是一样的,只不过使用第二种,可以保证类型转换是安全的,如果要转换成不符合的类型就会报错。static_......
  • 【C++指南】类和对象(四):类的默认成员函数——全面剖析 : 拷贝构造函数
     引言拷贝构造函数是C++中一个重要的特性,它允许一个对象通过另一个已创建好的同类型对象来初始化。了解拷贝构造函数的概念、作用、特点、规则、默认行为以及如何自定义实现,对于编写健壮和高效的C++程序至关重要。 C++类和对象系列文章,可点击下方链接阅读:【C++指南......
  • PTA 生成格雷码 | C++ | 二叉树
    格雷码是一种包含2n个数串的序列,这种序列:1不存在重复的元素,2每个元素都是长度为n的二进制数串,3相邻元素只有一位不同。例如,长度为23的格雷码为:000,001,011,010,110,111,101,100。请使用分治法构造格雷码。提示,使用分治法构造格雷码,详见百度百科。输入格式:输入一个正整数n(1<=......
  • 贪吃蛇编译就能玩c++
    #include<stdio.h>#include<conio.h>#include<iostream>#include<stdlib.h>#include<windows.h>#include<time.h>#defineframex2#defineframey2#definewide40#definehigh25usingnamespacestd;inti,a[2];intj=......
  • c++ STL标准模板库-算法
    C++StandardTemplateLibrary(STL)算法是一组泛型算法,它们可以在各种容器上操作。这些算法被设计为与容器无关,因此可以在任何提供必要迭代器接口的容器上使用。STL算法分为以下几个主要类别:非修改算法Non-modifyingsequenceoperations:不改变容器内容,主要用于搜索和排序。......
  • c++时间管理大师
    作者花了一个下午写出来的。c++写的时间管理大师。支持一下。#include<bits/stdc++.h>#include<windows.h>usingnamespacestd;constintpai=250;constintban=pai/2;#defineD1262#defineD2294#defineD3330#defineD4349#defineD5392#defineD6440......
  • 使用 C++ 实现网络编程 NetWork(pro版)
    上一次小编分享了C++实现网络编程NetWork,这是链接:C++网络通信类Network的实现详解-CSDN博客这次小编带着改进之后的NetWork归来!在现代计算机网络中,网络通信是不可或缺的一部分。本文将介绍如何使用C语言实现一个简单的网络通信库,涵盖TCP和UDP协议的基本功能。我们将通过......
  • 【代码】约瑟夫问题——c++解决代码
    哈喽大家好!我是@学霸小羊,上次讲了讲约瑟夫问题的背景,今天来讲讲怎样用代码实现。(还没了解背景小伙伴的可以先去下面的网址学习一下)【代码】约瑟夫问题——故事背景https://blog.csdn.net/yangyanbin_sam/article/details/143152232?spm=1001.2014.3001.5501首先,我们要将这个......