当前位置: 首页 > article >正文

【C++掌中宝】走进C++引用的世界:从基础到应用

在这里插入图片描述

文章目录

  • 引言
  • 1. 基础概念
  • 2. 引用与指针的区别
  • 3. 引用的应用场景
    • 3.1 引用作为函数参数
    • 3.2 引用作为函数返回值
    • 3.3 常引用(const引用)的使用
    • 3.4 引用与多态
  • 4. C++ 引用的优缺点
  • 5. 引用的注意事项与常见陷阱
  • 6. 总结
  • 结语

引言

C++ 引用是编写高效、简洁代码的重要工具。与指针类似,引用允许程序员以间接方式访问数据,但其使用方式更为安全和简便。在这篇博客中,我们将深入探讨 C++ 中引用的概念、特性以及使用方式,帮助你更好地理解和使用引用。

1. 基础概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。比如:水浒传中李逵,宋江叫"铁牛",江湖上人称"黑旋风";林冲,外号豹子头;

在这里插入图片描述

因此对引用的任何操作都会直接影响原变量。格式如下:

类型& 引用别名 = 引用对象;

引用使用 & 符号声明,例如:

int a = 10;
int& ref = a;

在上面的例子中,refa 的引用,二者共享同一块内存区域。修改 ref 的值会直接改变 a 的值。

引用的核心特性:

  • 必须初始化:引用在声明时必须绑定到某个变量,且不能更改绑定的对象。
  • 多引用:一个变量可以有多个引用
  • 不占用额外内存:引用和原变量共用同一内存空间。
  • 引用与变量操作一致:对引用的操作会反映在其绑定的变量上。

2. 引用与指针的区别

C++中指针和引用就像两个性格迥异的亲兄弟,指针是哥哥,引用是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有自己的特点,互相不可替代。

引用与指针都能实现间接访问,但二者存在以下不同点:

区别引用指针
初始化必须初始化可以延迟初始化
是否可更改指向不可更改可以更改
是否可为空不能为空(NULL)可以为空(NULL)
使用方式无需解引用符号需使用解引用符号
sizeof中的含义结果为引用类型的大小始终是地址空间所占字节个数(32位平台下占4个字节,64位平台下占8个字节)

引用的优势在于语法简洁且更安全,而指针则更灵活,但容易引发空指针或野指针问题。(从底层汇编的角度来看,引用也是用指针实现的,在底层实现上引用实际是有空间的

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

在这里插入图片描述

3. 引用的应用场景

引用在 C++ 编程中有广泛应用,尤其是在函数参数传递和返回值中。以下是几个常见应用场景:

3.1 引用作为函数参数

引用传递能避免值传递时的拷贝操作,提升效率,尤其在处理大对象时。例如:

void modify(int &x) {
    x = 10;  // 修改外部变量
}

函数 modify 直接修改了实参 x,而不是传递一个副本。

3.2 引用作为函数返回值

函数返回引用能够避免创建副本,直接操作外部数据:

int& getElement(int arr[], int index) {
    return arr[index];
}

返回数组元素的引用,使外部可以直接修改数组内容。

总结

  • 引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被引用对象。

  • 引用传参跟指针传参功能是类似的,引用传参相对更方便一些。

  • 引用返回值的场景相对比较复杂,我们在这里简单讲了一下场景,还有一些内容后续类和对象章节中会继续深入讲解。

  • 引用和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引用跟其他语言的引用(如Java)是有很大的区别的,除了用法,最大的点,C++引用定义后不能改变指向,Java的引用可以改变指向。

  • 一些主要用C代码实现版本数据结构教材中,使用C++引用替代指针传参,目的是简化程序,避开复杂的指针,但是很多同学没学过引用,导致一头雾水。

3.3 常引用(const引用)的使用

常引用 (const) 用于避免修改原数据,可以理解为只读型同时可以引用常量或临时对象。例如:

void display(const int &x) {
    std::cout << x << std::endl;
}

常引用保证了传递的数据不会被修改。


权限放大

//只读型(const)
const int a = 20;
 
//可读可写型  报错!
int &b= a;      × × ×
 
//权限平移
const int &c = a;   √ √ √ 

权限缩小

//可读可写型
int e = 30;
 
//只读型
const int &f = e;     √ √ √

临时变量具有常属性

类型转换,并不会改变变量类型,中间都会产生一个临时变量!!
//临时变量具有常属性 --- 相当于被const修饰了
int ii = 1;
//发生 -- 隐式转换 -- 
double dd = ii;        //隐式类型转换是语法允许的
 
--------------------------
 
//报错   权限被放大
double &rdd = ii;      // × × × 权限被放大了
//权限的平移
const double &rdd = ii;

在这里插入图片描述


使用反例

void func1(int  n)
{}
void func2(int& n)
{}
void func3(const int& n)
{}
 
int main()
{
	int a = 10;
	const int b = 20;
	func1(a);
	func1(b);
	func1(30);
 
	func2(a);
 
	//权限被放大   -- 报错 --
	func2(b);
	//权限被放大   -- 报错 --
	func2(30);
	
	double d = 2.22;
	//权限被放大   -- 报错 --
	func2(d);
	//语法允许
	func3(d);
	return 0;
}

总结

  • 可以引用一个const对象,但是必须用const引用。const引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,但是不能放大

  • 权限放大和缩小只针对引用和指针

  • 不需要注意的是类似 int& rb = a*3; double d = 12.34; int& rd = d; 这样一些场景下a*3的和结果保存在一个临时对象中, int& rd = d 也是类似,在类型转换中会产生临时对象存储中间值,也就是时,rb和rd引用的都是临时对象,而C++规定临时对象具有常性,所以这里就触发了权限放大,必须要用常引用才可以。

  • 所谓临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象,C++中把这个未命名对象叫做临时对象。

3.4 引用与多态

在多态场景中,引用可以通过基类指向派生类,实现多态行为:

class Base {
public:
    virtual void show() { std::cout << "Base" << std::endl; }
};

class Derived : public Base {
public:
    void show() override { std::cout << "Derived" << std::endl; }
};

void display(Base &obj) {
    obj.show();  // 基类引用可指向派生类
}

4. C++ 引用的优缺点

优点

  • 语法简化,操作更加直观。
  • 避免空指针或野指针问题。
  • 减少拷贝,提高函数调用效率。

缺点

  • 必须在声明时初始化,灵活性不如指针。
  • 一旦初始化不能更改指向对象。
  • 使用不当时可能导致悬空引用。

5. 引用的注意事项与常见陷阱

  • 不要返回局部变量的引用:局部变量在函数结束后会被销毁,返回其引用会导致悬空引用。
  • 避免引用无效内存:确保引用指向合法的内存空间,避免运行时错误。
  • 引用与指针的混用:引用虽然简化了很多操作,但并不能完全替代指针,特别是在涉及动态内存管理时。

6. 总结

C++ 引用是简化代码、提高程序效率的重要工具,特别是在参数传递、返回值优化等场景中发挥了重要作用。与指针相比,引用更加简洁、安全,但在灵活性上有所限制。通过正确理解和使用引用,可以让你的 C++ 代码更加高效、稳健。

结语

希望这篇博客能帮助你深入理解 C++ 中的引用。祝你在编程的旅途中不断进步!如有任何问题或需要进一步讨论的内容,欢迎在评论区留言交流!

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。

也可以点点关注,避免以后找不到我哦!

Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

在这里插入图片描述

参考:【C++】之引用详解 什么是引用?_c++引用-CSDN博客


http://www.kler.cn/a/318076.html

相关文章:

  • 基于OpenCV的自制Python访客识别程序
  • 封装一个省市区的筛选组件
  • 解锁微前端的优秀库
  • apache2配置多站点
  • Spring Boot 中的全局异常处理器
  • C++ 的协程
  • leveldb前缀匹配查找Seek
  • SWC(Speedy Web Compiler)
  • java算法OJ(1)位运算
  • LabVIEW闪退
  • 企业职工薪资查询系统小程序的设计
  • JVM —— 类加载器的分类,双亲委派机制
  • 6 门新兴语言,小众亦强大
  • SpringCloud 基于 web 的只会养老平台
  • 【30天玩转python】高级面向对象编程
  • MYSQL解说
  • Flexus X实例全方位指南:智能迁移、跨云搬迁加速与虚机热变配能力的最佳实践
  • Linux——创建编写并编译一个C程序
  • 前端项目代码开发规范及工具配置
  • 【Linux】深度解析与实战应用:GCC/G++编译器入门指南
  • 无人机视角下的车辆数据集
  • 18.1 k8s服务组件之4大黄金指标讲解
  • 高等数学的后续课程
  • [大语言模型] LINFUSION:1个GPU,1分钟,16K图像
  • 个人量化成功之路-----获取实时OHLC的数据
  • 设计模式六大原则:面向对象设计的核心