C++初阶(二)--C++入门(引用篇)

目录

一、引用的基本概念与特性

  1.定义与声明

 2.特性

二、引用的进阶用法

1.函数参数传递:

2.引用作为函数返回值(重点)

引用作为返回值的优点

引用作为返回值的注意事项

代码示例

注意事项的进一步说明

三、传值和传引用效率比较

1.值和引用的作为参数的性能比较

2.值和引用的作为返回值类型的性能比较 

四、 引用和指针的区别


在C++编程的广阔天地中,引用是一种强大且独特的工具,它允许程序员为已存在的变量创建别名,通过这个别名可以直接访问和操作原始变量。引用的这一特性不仅简化了代码,提高了代码的可读性,还带来了性能上的优势。接下来,我们将对C++中的引用进行深入剖析,探讨其工作机制、应用场景以及需要注意的事项,并通过具体代码示例进行说明。

一、引用的基本概念与特性

  1.定义与声明

  • 引用是C++中对某一变量(目标变量)的别名。通过引用,我们可以直接访问和操作原始变量,而无需通过指针的间接访问方式。
  • 在C++中,使用&符号来声明引用。其基本语法为:

        类型标识符& 引用名 = 已存在的变量名;

int main()
{
	int a = 10;
	int& ra = a;//ra 就是 a 的别名
	int& x = a;
	int& y = x;
}

 2.特性

  • 必须初始化:引用在声明时必须被初始化,且一旦初始化后,其引用的对象不能改变。即引用不能重新绑定到另一个变量上。
  • 不占内存:从概念上讲,引用本身不占用内存空间,因为它只是原始变量的一个别名。但在底层实现上,编译器通常会将引用实现为指向原始变量的常量指针(const pointer),因此实际上会占用指针大小的内存。
  • 类型一致:引用的类型必须与它所引用的变量的类型一致。

二、引用的进阶用法

1.函数参数传递

  • 通过引用传递函数参数可以避免数据的复制:做输出型参数,对象比较大时减少拷贝从而提高函数的执行效率。
void swap(int& x, int& y) {  
    int temp = x;  
    x = y;  
    y = temp;  
}  

int main() {  
    int a = 5, b = 10;  
    swap(a, b);  
    std::cout << "a: " << a << ", b: " << b << std::endl; // 输出a: 10, b: 5  
    return 0;  
}

2.引用作为函数返回值(重点)

引用作为返回值的优点
  1. 避免拷贝:当函数返回大型对象或容器时,如果直接返回对象本身,会导致对象的拷贝。而返回引用则可以避免这种不必要的拷贝,从而提高效率。

  2. 允许修改:通过返回引用,调用者可以修改被返回对象的状态。这在某些情况下是非常有用的,比如当你需要实现一个返回容器中某个元素的函数时。

  3. 支持链式操作:返回引用允许实现链式操作,即连续调用返回引用的成员函数。

引用作为返回值的注意事项
  1. 确保对象存在:返回引用时,必须确保被引用的对象在函数返回后仍然有效。如果返回的是局部变量的引用,那么当函数结束时,局部变量会被销毁,返回的引用将指向一个无效的内存位置,这会导致未定义行为。

  2. 避免返回非常量引用:除非有充分的理由,否则应避免返回非常量引用,因为这可能会允许调用者修改被返回对象的状态,从而引入潜在的错误和不可预测的行为。如果确实需要返回可修改引用,应确保调用者明白这一点,并小心处理。

  3. 考虑使用常量引用:当不需要修改被返回对象时,应优先考虑返回常量引用。这不仅可以保护对象不被修改,还可以提高代码的可读性和安全性。

代码示例

下面是一个返回常量引用的示例,该示例从std::vector中查找并返回最大元素的引用(作为常量,因为不希望调用者修改它):

#include <iostream>  
#include <vector>  
#include <algorithm> // for std::max_element  

const int& findMax(const std::vector<int>& vec) 
{
    // 使用std::max_element找到最大元素的迭代器  
    auto it = std::max_element(vec.begin(), vec.end());
    // 返回最大元素的引用(作为常量)  
    return *it;
}

int main() 
{
    std::vector<int> nums = { 1, 3, 7, 2, 9, 5 };
    const int& maxNum = findMax(nums);
    std::cout << "The maximum number is " << maxNum << std::endl;
    // 注意:不能修改maxNum,因为它是常量引用  
    // maxNum = 100; // 这会导致编译错误  
    return 0;
}

 在这个例子中,findMax函数返回了一个常量引用,指向vector中的最大元素。由于返回的是常量引用,调用者不能修改被返回的元素。

注意事项的进一步说明
  • 避免返回局部变量的引用:如前所述,这是非常危险的,因为局部变量在函数结束时会被销毁。

  • 考虑对象的生命周期:返回引用时,应确保被引用的对象在整个程序运行期间都是有效的。例如,如果函数返回了一个指向堆上分配对象的引用,那么调用者必须负责在适当的时候释放该对象。

  • 使用智能指针:在某些情况下,使用智能指针(如std::shared_ptrstd::unique_ptr)作为返回值可能是一个更好的选择。这可以自动管理对象的生命周期,并避免悬垂引用的问题。然而,这也增加了代码的复杂性,并可能引入其他潜在的问题,如循环引用和性能开销。因此,在使用智能指针时应谨慎考虑。

三、传值和传引用效率比较

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

1.值和引用的作为参数的性能比较

#include <time.h>
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 < 100000; ++i)
		TestFunc1(a);
	size_t end1 = clock();
	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; ++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;
}

 

 从运行时间可以看出,传引用的效率更高。

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;
	
}


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

 

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

四、 引用和指针的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。

指针和引用的功能是类似的,重叠的。

C++的引用,对指针使用比较复杂的场景进行一些替换,让代码更简单易懂,但是不能完全替代指针。

引用不能完全替代指针的原因:引用定义后,不能改变指向(在链表的实现中就需要经常改变指向)。

int main()
 {
 int a = 10;
 int& ra = a;
 cout<<"&a = "<<&a<<endl;
 cout<<"&ra = "<<&ra<<endl;
 return 0;
 }

 在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

int main()
{
	int a = 10;
	int& ra = a;
	ra = 20;
	int* pa = &a;
	*pa = 20;
	return 0;
}

我们来看下引用和指针的汇编代码对比: 

总结:

引用和指针的不同点:

1、定义与基本概念

引用:

引用在C++等编程语言中是一个重要的概念,它相当于某个变量的别名。

引用必须在声明时被初始化,且一旦被初始化后,就不能再改变引用的对象(但对象的值可以改变)。

指针:

指针是一个变量,其存储的是另一个变量的内存地址。

指针可以在任何时候被初始化,且可以随时改变其指向的对象。

2、内存与访问方式

引用:

引用本身不占用内存空间,它只是对象的别名。

对引用的操作实际上是对原对象的直接操作。

指针:

指针本身占用一定的内存空间,用于存储地址信息。

通过指针访问对象时,需要先解引用(即使用“*”操作符),才能访问指针所指向的对象。

3、特性与安全性

引用:

引用不能为空,它必须与合法的存储单元关联。

引用是类型安全的,编译器会对引用进行类型检查。

由于引用不能改变指向的对象,因此它在一定程度上比指针更安全。

指针:

指针可以为空(即指向NULL),也可以指向非法的内存地址(野指针)。

指针的类型安全性不如引用,因为编译器不会对指针进行严格的类型检查。

指针的灵活性更高,但也更容易出错,因此在使用时需要更加小心。

4、使用场景与示例

引用:

引用常用于函数参数传递和返回值,以避免不必要的拷贝和提高效率。

示例:void swap(int &a, int &b),在这个函数中,a和b都是对实参的引用,交换它们的值实际上是在交换实参的值。

指针:

指针常用于动态内存分配、数组操作、链表等数据结构以及函数指针等高级用法。

示例:int *p = new int(10);,这里p是一个指向整数的指针,它指向了一个动态分配的整数对象。

5、汇编层面的实现

从汇编层面来看,引用和指针在实现上有一定的相似性。例如,在C++中,引用在底层通常是通过指针来实现的。但是,编译器会对引用进行特殊的处理,以确保其安全性和类型安全性。因此,尽管引用和指针在汇编层面可能有一定的相似性,但在高级语言层面,它们的使用方式和特性是有显著区别的。

综上所述,引用和指针在编程中各有其独特的特性和使用场景。理解它们的区别和联系对于编写高效、安全的代码至关重要。 


 如有错误之处,望评论区指正

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

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

相关文章

华三服务器R4900 G5在图形界面使用PMC阵列卡(P460-B4)创建RAID,并安装系统(中文教程)

环境以用户需求安装Centos7.9&#xff0c;服务器使用9块900G硬盘&#xff0c;创建RAID1和RAID6&#xff0c;留一块作为热备盘。 使用笔记本通过HDM管理口&#xff08;&#xff09;登录 使用VGA&#xff08;&#xff09;线连接显示器和使用usb线连接键盘鼠标&#xff0c;进行窗…

10月报名 | 海克斯康Adams二次开发培训

您好&#xff01;感谢您长期以来对优飞迪科技与海克斯康的关注与支持。我们诚邀您参加10月31日-11月1日的海克斯康Adams二次开发培训&#xff0c;本次培训将通过讲解和实操结合的方式&#xff0c;帮助用户了解Adams二次开发技术&#xff0c;学习Adams命令语言&#xff0c;掌握如…

[自然语言处理]RNN

1 传统RNN模型与LSTM import torch import torch.nn as nntorch.manual_seed(6)# todo:基础RNN模型 def dem01():参数1&#xff1a;input_size 每个词的词向量维度&#xff08;输入层神经元的个数&#xff09;参数2&#xff1a;hidden_size 隐藏层神经元的个数参数3&#xff1a…

基于Python的博客系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

智能化企业新人培训:AI助理如何加速新员融入与成长

在当今这个快速变化的时代&#xff0c;企业新人的培训不再仅仅局限于传统的教室环境&#xff0c;而是越来越多地融入了先进的技术&#xff0c;特别是人工智能&#xff08;AI&#xff09;。AI助理&#xff0c;作为这一变革的先锋&#xff0c;正在以独特的方式重塑企业新人培训的…

废水处理(一)——MDPI特刊推荐

特刊征稿 01 期刊名称&#xff1a; Removing Challenging Pollutants from Wastewater: Effective Approaches 截止时间&#xff1a; 摘要提交截止日期&#xff1a;2024年11月30日 投稿截止日期&#xff1a;2025年5月31日 目标及范围&#xff1a; 该主题是分享去除有毒物…

TQRFSOC开发板47DR 100G光口ping测试

本例程实现TQRFSOC开发板使用100G光口与100G网卡进行ping测试。TQRFSOC开发板有两个100G光口&#xff0c;都将进行测试&#xff0c;所使用的100G网卡同样是我们生产的&#xff0c;有需要的可以配套进行购买。本例程提供两个启动文件&#xff0c;分别对应两个光口&#xff0c;通…

4D-fy: Text-to-4D Generation Using Hybrid Score Distillation Sampling技术路线

这篇文章分为四部分&#xff0c;首先从2021年的CLIP说起。 这篇论文的主要工作是提出了一种名为 CLIP&#xff08;Contrastive Language-Image Pre-training&#xff09; 的模型&#xff0c;它通过自然语言监督学习视觉模型&#xff0c;以实现视觉任务的零样本&#xff08;zer…

「规模焦虑」如影随形,库迪咖啡想靠便捷店突围能行吗?

作者 | 辰纹 来源 | 洞见新研社 “我有一个广东的小兄弟&#xff0c;做了9年的奶茶&#xff0c;后来因为觉得咖啡是一个上升期的赛道&#xff0c;所以毅然决然拿了45万加盟了库迪咖啡&#xff0c;结果全亏损完了&#xff0c;相当于只买了一个配方。” 抖音博主茶饮圈大山哥分…

MyBatis XML映射文件

XML映射文件 XML映射文件的名称与Mapper接口名称一致&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09;XML映射文件的namespace属性为Mapper接口全限定名一致XML映射文件中SQL语句的id与Mapper接口中的方法名一致&#xff0c;并保持返…

C语言_指针_进阶

引言&#xff1a;在前面的c语言_指针初阶上&#xff0c;我们了解了简单的指针类型以及使用&#xff0c;下面我们将进入更深层次的指针学习&#xff0c;对指针的理解会有一个极大的提升。从此以后&#xff0c;指针将不再是难点&#xff0c;而是学习底层语言的一把利器。 本章重点…

ubuntu 开放 8080 端口快捷命令

文章目录 查看防火墙状态开放 80 端口开放 8080 端口开放 22端口开启防火墙重启防火墙**使用 xhell登录**&#xff1a; 查看防火墙状态 sudo ufw status [sudo] password for crf: Status: inactivesudo ufw enable Firewall is active and enabled on system startup sudo…

Linux性能调优,还可以从这些方面入手

linux是目前最常用的操作系统&#xff0c;下面是一些常见的 Linux 系统调优技巧&#xff0c;在进行系统调优时&#xff0c;需要根据具体的系统负载和应用需求进行调整&#xff0c;并进行充分的测试和监控&#xff0c;以确保系统的稳定性和性能。同时&#xff0c;调优过程中要谨…

第二十一节 图像旋转

void QUickdemo::roate_demo(Mat& image) { Mat dst, M; int w image.cols; int h image.rows; M getRotationMatrix2D(Point2f(w / 2, h / 2), 45, 1.0);--M getRotationMatrix2D(Point2f(w / 2, h / 2), 45, 1.0);&#xff1a;使用getRotationMatrix…

Linux学习网络编程学习(TCP和UDP)

文章目录 网络编程主要函数介绍1、socket函数2、bind函数转换端口和IP形式的函数 3、listen函数4、accept函数网络模式&#xff08;TCP&UDP&#xff09;1、面向连接的TCP流模式2、UDP用户数据包模式 编写一个简单服务端编程5、connect函数编写一个简单客户端编程 超级客户端…

jmeter入门:脚本录制

1.设置代理。 网络连接-》代理-》手动设置代理. ip&#xff1a; 127.0.0.1&#xff0c; port&#xff1a;8888 2. add thread group 3. add HTTP(s) test script recorder, target controller chooses Test plan-> thread Group 4. click start. then open the browser …

Windows环境下Qt Creator调试模式下qDebug输出中文乱码问题

尝试修改系统的区域设置的方法&#xff1a; 可以修复问题。但会出现其它问题&#xff1a; 比如某些软件打不开&#xff0c;或者一些软件界面的中文显示乱码&#xff01; 暂时没有找到其它更好的办法。

k8s的微服务

ipvs模式 Service 是由 kube-proxy 组件&#xff0c;加上 iptables 来共同实现的 kube-proxy 通过 iptables 处理 Service 的过程&#xff0c;需要在宿主机上设置相当多的 iptables 规则&#xff0c;如果宿主机有大量的Pod&#xff0c;不断刷新iptables规则&#xff0c;会消耗…

FreeRTOS应用开发学习

了解FreeRTOS 任务相关API FreeRTOS任务创建API FreeRTOS 中&#xff0c;任务的创建有两种方法&#xff0c;一种是使用动态创建&#xff0c;一种是使用静态创建。动态创建时&#xff0c;任务控制块和栈的内存是创建任务时动态分配的&#xff0c;任务删除时&#xff0c;内存可…

推动AI技术研发与应用,景联文科技提供专业高效图像采集服务

景联文科技提供专业图像采集服务&#xff0c;涵盖多个领域的应用需求。 包含人体图像、人脸图像、手指指纹、手势识别、交通道路、车辆监控等图像数据集&#xff0c;计算机视觉图像数据集超400TB&#xff0c;支持免费试采试标。 高质量人像采集服务&#xff1a;支持不同光线条件…