C++从入门到实战(四)C++引用与inline,nullptr

C++从入门到实战(四)C++引用与inline,nullptr


前言

  • 在上一篇博客中,我们深入探讨了 C++ 中两个重要特性:缺省参数与函数重载

上一篇博客地址
https://blog.csdn.net/2402_83322742/article/details/145355328?spm=1001.2014.3001.5501

缺省参数让函数调用更加简便,维护更为轻松;函数重载则增强了代码的可读性和灵活性。通过掌握这两个特性,我们能够编写出更具灵活性和高效性的代码。

  • 这一篇,我们将继续探索 C++ 中另外几个重要的知识点:C++ 引用、inline 关键字以及 nullptr。这些内容将进一步丰富我们对 C++ 语言的理解,为编写更加复杂和高效的程序提供有力支持。

一、C++ 引用

(一)什么是引用

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

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

#include <iostream>
using namespace std;int main()
{int a = 0;int& b = a;int& c = a;//对d相当于还是a的引用int& d = b;cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << &d << endl;return 0;
}

上述代码中,b 是 a 的引用,对 b 的操作等同于对 a 的操作,对d相当于还是a的引用

(二)引用的特点

  • 1.必须初始化:引用在定义时必须初始化,即必须指定它是哪个变量的别名。
// 错误示例
int& ref; 
  • 2.一旦初始化后,就不能再引用其他变量
int a = 10;
int b = 20;
int& ref = a;
// ref = b; // 这里不是让 ref 引用 b,而是将 b 的值赋给 ref(也就是 a)
  • 3.一个变量可以有多个引用
#include<iostream>
using namespace std;
int main()
{int a = 10;// 编译报错: “ra”: 必须初始化引用//int& ra;int& b = a;int c = 20;// 这里并非让b引用c, 因为C++引用不能改变指向,// 这里是一个赋值b = c;cout << &a << endl;cout << &b << endl;cout << &c << endl;return 0;
}

(三)引用作为函数参数

引用作为函数参数,可以在函数内部修改传入变量的值,而且相比于指针,代码更加简洁和安全。

void swap(int& a, int& b) {int temp = a;a = b;b = temp;
}int main() {int num1 = 5;int num2 = 10;swap(num1, num2);std::cout << "num1: " << num1 << ", num2: " << num2 << std::endl; return 0;
}

在这个 swap 函数中,通过引用参数直接操作实参,实现了两个数的交换。

(四)引用作为函数返回值

引用也可以作为函数的返回值,这样函数返回的是一个变量的引用,而不是变量的副本。

int arr[5] = {1, 2, 3, 4, 5};
int& getElement(int index) 
{return arr[index];
}int main() 
{getElement(2) = 10; std::cout << "arr[2]: " << arr[2] << std::endl; return 0;
}

(五)引用的使用

  • 1.C++ 中引用在实际应用里,主要用在引用传参和引用做返回值的场景。

引用传参
比如下面这个交换两个数的函数,使用引用传参能直接改变传入的实参值,还减少了数据拷贝,提高效率。而且改变引用对象时,被引用对象也会跟着变。

#include <iostream>
void swap(int& a, int& b) {int temp = a;a = b;b = temp;
}
int main() {int num1 = 5;int num2 = 10;swap(num1, num2);std::cout << "num1: " << num1 << ", num2: " << num2 << std::endl;return 0;
}
    1. 引用传参和指针传参对比

引用传参和指针传参功能差不多,但引用传参用起来更方便。指针传参如下

#include <iostream>
void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}
int main() {int num1 = 5;int num2 = 10;swap(&num1, &num2);std::cout << "num1: " << num1 << ", num2: " << num2 << std::endl;return 0;
}

可以看到,引用传参在函数调用时不需要像指针那样取地址(&),在函数内部使用时也不需要像指针那样解引用(*) 。

  • 3.引用和指针对比

引用和指针在实际编程中都很重要,功能有重叠但各有特点,不能互相替代。C++ 的引用和 Java 的引用区别较大,C++ 引用定义后不能改指向,Java 的可以。

二、Const引用

(1)const 引用是干什么的

const 引用就是加了 “规矩” 的引用。这个 “规矩” 就是:不能通过这个引用去修改它引用的对象。就好像你给一个东西上了把锁,只能看不能动。

(2)引用 const 对象

  • 假如有个变量是 const 的,这就说明这个变量是 “只读” 的,不能被修改。
  • 你要是想用引用去访问它,也得用 const 引用
  • 打个比方,有个宝箱被施了魔法,只能看不能打开(const 变量),你要是想用一个能打开宝箱的钥匙(普通引用)去开它,肯定不行啊,这就违反魔法规则了。代码里就是这样:

const int a = 10;
// 下面这行代码会报错,因为普通引用就像能打开宝箱的钥匙,违反了魔法规则
// int& ra = a; 
const int& ra = a; // 用 const 引用就对啦,就像用了一把只能看宝箱的钥匙

(3)const 引用引用普通对象

  • const 引用也可以引用普通对象

  • 这就好比一个普通人(普通变量),你给他戴上一个 “不能随便乱动” 的帽子(const 引用),虽然他本来可以自由活动,但戴上帽子后就只能乖乖待着了。这其实就是访问权限的缩小,是允许的。

int b = 20;
const int& rb = b; // 给 b 戴上“不能随便乱动”的帽子
// 下面这行代码会报错,因为不能通过 rb 去修改 b
// rb = 30; 

(4)引用和指针的关系

  • 指针和引用就像 C++ 编程世界里的两兄弟,它们既有相似之处,又各有特点

3.1相同点

  • 指针和引用都和变量相关,能用来操作其他变量,功能上有重叠,都可以间接访问别的变量。
  • 比如,你想找到房间里的某样东西,既可以通过知道这个东西所在的房间号(指针指向变量地址),也可以通过记住这个东西的另一个称呼(引用给变量取别名)来找到它。

3.2不同点

  • 1.本质不同

引用:就像是给变量取了个外号,它和被引用的变量其实是同一个东西,不需要额外的存储空间。比如一个人叫张三,大家也叫他小张,小张和张三指的就是同一个人。
指针:是专门用来存放变量地址的,需要开辟额外的存储空间。就好比一个小本子,上面记录着某个东西所在的房间号。

  • 2.初始化要求不同

引用:就像给人取外号,得一开始就明确是给谁取的,所以引用在定义的时候必须进行初始化,之后就不能再换 “对象” 了。比如你一开始给张三取了小张这个外号,就不能再把小张这个外号给别人了。
指针:虽然建议在定义的时候就初始化,但语法上不是必须的。就像小本子你可以先空着,等需要的时候再记录房间号。不过不初始化可能会导致一些问题。

  • 3.指向的灵活性不同

引用:一旦确定引用的对象,就不能再引用其他对象了,它会 “从一而终”。就像外号一旦确定给了某个人,就不能再给别人用了。
指针:可以不断改变指向的对象,就像小本子上可以随时擦掉原来的房间号,写上新的房间号。

  • 4.访问方式不同

引用:可以直接访问指向的对象,因为它就是对象的别名,就像叫小张其实就是在叫张三,能直接找到这个人。
指针:需要通过解引用操作才能访问指向的对象,就像你只知道房间号,得拿着这个号去找到对应的房间,才能拿到里面的东西。

  • 5.sizeof 结果不同

引用:sizeof 得到的是引用类型的大小,也就是被引用对象的大小。就像问小张占多大空间,其实就是问张三占多大空间。
指针:sizeof 得到的是地址空间所占的字节数,在 32 位平台下占 4 个字节,64 位平台下占 8 个字节。不管指针指向的是什么类型的对象,它记录的都是地址,所以大小是固定的,就像小本子不管记录的是哪个房间号,它本身的大小是不变的。

  • 6.安全性不同

引用:很少出现空引用和类似指针的错误情况,使用起来相对更安全,因为它一开始就确定了引用的对象,而且不会改变。就像外号不会叫错人。
指针:容易出现空指针(小本子上没写房间号)和野指针(小本子上写的房间号是错的或者已经无效了)的问题,使用时需要格外小心。

三、nullptr

(一)nullptr 是什么

  • 在 C++ 中,nullptr 是一个关键字,用于表示空指针。
  • 在 C++ 之前,通常用 NULL 来表示空指针,NULL 实际上是一个宏定义,在不同的编译器中可能被定义为 0 或者 (void*)0。而 nullptr 专门用于表示空指针,类型为 std::nullptr_t,可以避免一些与 0 或 NULL 相关的二义性问题。例如:
int* ptr1 = nullptr;
// int* ptr2 = NULL; // 虽然也能表示空指针,但可能存在二义性

(二)nullptr 与 NULL 的区别

类型安全:nullptr 具有明确的类型 std::nullptr_t,而 NULL 可能被定义为 0,这可能导致一些重载函数调用的二义性。例如:

void func(int num) 
{std::cout << "func(int): " << num << std::endl;
}
void func(int* ptr) {std::cout << "func(int*): " << (ptr? "not null" : "null") << std::endl;
}
int main() {func(nullptr); // 明确调用 func(int*)// func(NULL); // 可能会调用 func(int),产生二义性return 0;
}

四、inline 关键字

(一)什么是 inline 函数

  • inline 关键字用于定义内联函数。
  • 内联函数的作用是在编译时,将函数调用处用函数体替换,减少函数调用的开销,提高程序执行效率。
#include <iostream>// 定义一个内联函数,用于计算两个整数的和
inline int add(int a, int b) {return a + b;
}int main() {int x = 3, y = 5;// 调用内联函数int result = add(x, y); std::cout << "The result is: " << result << std::endl;return 0;
}

(二)与C语言宏函数对比

C 语言宏函数:宏函数只是简单的文本替换,不会进行类型检查。

// 定义宏函数
#define ADD(a, b) ((a) + (b))
#define SQUARE(x) ((x) * (x))
// 调用时传入字符串,预处理器仍会进行替换,可能导致运行时错误
char* str = "test";
int res = SQUARE(str); 

C++ 内联函数作为真正的函数,C++ 内联函数会进行严格的类型检查。

inline double square(double num) {return num * num;
}
// 调用时,如果传入整数,会进行隐式类型转换
int result = square(5); 

C++ 内联函数:内联函数是真正的函数,在调试时可以像普通函数一样进行调试,能够方便地设置断点、查看变量值等。而且内联函数的代码结构清晰,易于维护和理解。例如,使用调试器可以单步进入内联函数的代码。
C 语言宏函数:宏函数的调试相对困难,因为预处理器在编译前就完成了文本替换,调试器看到的是替换后的代码,很难直接定位到宏定义的位置。同时,宏函数的代码可能会因为复杂的替换规则而变得难以理解和维护。


以上就是这篇博客的全部内容,下一篇我们将继续探索 C++ 的更多精彩内容。

非常感谢您的阅读,喜欢的话记得三连哦

在这里插入图片描述

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

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

相关文章

【C/C++算法】从浅到深学习--- 二分查找(图文兼备 + 源码详解)

绪论&#xff1a;冲击蓝桥杯一起加油&#xff01;&#xff01; 每日激励&#xff1a;“不设限和自我肯定的心态&#xff1a;I can do all things。 — Stephen Curry” 绪论​&#xff1a; 本章是算法篇章的第三章二分算法&#xff0c;本章主要是通过题目的形式来进行学习&…

mysql之联合索引

文章目录 一&#xff1a;联合索引二&#xff1a;创建联合索引三&#xff1a;删除索引四&#xff1a;总结&#xff1a; 一&#xff1a;联合索引 联合索引又称组合索引或者复合索引&#xff0c;是建立在俩列或者多列以上的索引。 二&#xff1a;创建联合索引 语法&#xff1a…

51单片机09 DS1302时钟

测试一 测试代码&#xff1a;别忘了之前调整点阵的跳线 #include <STC89C5xRC.H> #include "LCD1602.h"void main() {LCD_Init();LCD_ShowString(1,1,"RTC");while(1){} } ------------------------------------ 测试二 DS1302.C #include &l…

【前端OCR】如何用paddlejs开发一个属于前端本地的OCR文本识别功能

之前出过一篇关于用tesseract纯前端实现文本识别功能的文档&#xff0c;经测试之后&#xff0c;用是能用&#xff0c;但识别准确率并不高&#xff0c;而且耗时也相对比较久。 于是又找了一个paddlejs做开发测试&#xff0c;但是整体上来说&#xff0c;其实两个差不多。而且初始…

Spring IoC的实现机制是什么?

大家好&#xff0c;我是锋哥。今天分享关于【Spring IoC的实现机制是什么&#xff1f;】面试题。希望对大家有帮助&#xff1b; Spring IoC的实现机制是什么&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring IoC&#xff08;Inversion of Control…

Web3 开发者周刊 36 | 构建自主未来:Agent、可扩展性与赏金

欢迎来到 Web3 开发者周刊 36&#xff0c;这里汇聚了赋能您的 Web3 构建之旅的各种资源。本周我们将剖析基于Agent的系统&#xff0c;讨论来自 Vitalik 关于以太坊 L1 和 L2 的最新思考&#xff0c;并提供最新高价值Bounty消息。 开始Build吧&#xff01; ✅ One Trillion Age…

网络安全-防御 第一次作业(由于防火墙只成功启动了一次未补截图)

防火墙安全策略课堂实验报告 一、拓扑 本实验拓扑包含预启动设备、DMZ区域&#xff08;含OA Server和Web Server&#xff09;、防火墙&#xff08;FW1&#xff09;、Trust区域&#xff08;含办公区PC和生产区PC&#xff09;等。具体IP地址及连接关系如给定拓扑图所示&#xf…

Vue.js 与低代码开发:如何实现快速应用构建

在当今数字化时代&#xff0c;企业对应用开发的效率要求越来越高。传统开发模式往往耗时费力&#xff0c;难以满足快速变化的市场需求。而 Vue.js 与低代码开发的结合&#xff0c;为快速构建应用提供了新的解决方案&#xff0c;让企业能够更敏捷地响应市场变化&#xff0c;抢占…

第39周:猫狗识别 2(Tensorflow实战第九周)

目录 前言 一、前期工作 1.1 设置GPU 1.2 导入数据 输出 二、数据预处理 2.1 加载数据 2.2 再次检查数据 2.3 配置数据集 2.4 可视化数据 三、构建VGG-16网络 3.1 VGG-16网络介绍 3.2 搭建VGG-16模型 四、编译 五、训练模型 5.1 上次程序的主要Bug 5.2 修改版…

朝天椒USB服务器解决前置机U盾虚拟机远程连接

本文探讨朝天椒USB服务器用Usb Over Network技术&#xff0c;解决前置机虚拟化部署后U盾的远程连接问题。 在金融、电信等关键行业&#xff0c;后台核心处理系统承担着至关重要的业务数据交互职责。为保障系统安全&#xff0c;这些单位要求企业通过前置机与他们的内网进行数据…

《刚刚问世》系列初窥篇-Java+Playwright自动化测试-23- 操作鼠标拖拽 - 番外篇(详细教程)

拉票 亲爱的小伙伴们或者童鞋们&#xff0c;喜欢宏哥文章的&#xff0c;请动动你们发财小手&#xff0c;给我投投票票 。 祝2025小伙伴们工作顺利&#xff0c;家庭和睦&#xff0c;心想事成&#xff0c;财源滚滚&#xff01; 我的票还有7票&#xff0c;互票的朋友私信给我。 投…

教程 | 从零部署到业务融合:DeepSeek R1 私有化部署实战指南

文章目录 1. 什么是 DeepSeek R1&#xff1f;a. 主要介绍a. 版本区别 2. 部署资源要求a. 硬件资源要求 3. 本地安装DeepSeek-R1a. 为什么选择本地部署&#xff1f;b. 部署工具对比c. 演示环境配置d. Ollama安装流程 4. 可视化工具a. 工具对比b. Open-WebUI部署 5. AI API应用a.…

学习总结2.14

深搜将题目分配&#xff0c;如果是两个题目&#xff0c;就可以出现左左&#xff0c;左右&#xff0c;右左&#xff0c;右右四种时间分配&#xff0c;再在其中找最小值&#xff0c;即是两脑共同处理的最小值 #include <stdio.h> int s[4]; int sum0; int brain[25][25]; …

Qt Creator 5.0.2 (Community)用久了突然变得很卡

目录 1.现象 2.解决方案 1.现象 很久没有用Qt Creator开发项目了&#xff0c;刚刚结束的项目又是用VS2019开发的&#xff1b;这两天刚好有时间去学习一下Qt&#xff0c;刚好要用Qt Creator&#xff0c;结果一打开就没反应&#xff0c;主界面显示出来要好几分钟&#xff0c;最…

DeepSeek的深度解析:由来、研发过程、公司背景、优势、劣势与总结

DeepSeek的由来 DeepSeek&#xff0c;中文名“深度求索”&#xff0c;是一个在人工智能领域崭露头角的创新项目。其英文名“DeepSeek”由“深思”&#xff08;Deep&#xff09;与“探索”&#xff08;Seek&#xff09;组合而成&#xff0c;寓意着凭借深度学习技术不断探索未知…

初阶c语言(练习题,猜随机数)

前言&#xff1a; 学习c语言&#xff0c;学习来源b站鹏哥&#xff0c;37天吧应该是 内容&#xff1a; 这集内容挺多&#xff0c;源代码放到文章最后 题目是&#xff0c;使用函数编写一个随机数&#xff0c;然后自己猜&#xff0c;猜随机数 这里囊括了很多的知识点&#xf…

w206基于Spring Boot的农商对接系统的设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

Python PyCharm DeepSeek接入

Python PyCharm DeepSeek接入 创建API key 首先进入DeepSeek官网,https://www.deepseek.com/ 点击左侧“API Keys”,创建API key,输出名称为“AI” 点击“创建",将API key保存,复制在其它地方。 在PyCharm中下载Continue插件 安装 下载中 下载完成后,点击OK 配…

鸿蒙开发:了解@Builder装饰器

前言 本文代码案例基于Api13&#xff0c;温馨提示&#xff1a;内容相对来说比较简单&#xff0c;如果您已掌握&#xff0c;略过即可。 如果说一个页面中组件有很多&#xff0c;我们都统一写到build函数中&#xff0c;显而易见&#xff0c;会导致build函数代码非常冗余&#xff…

一文深入了解DeepSeek-R1:模型架构

本文深入探讨了 DeepSeek-R1 模型架构。让我们从输入到输出追踪 DeepSeek-R1 模型&#xff0c;以找到架构中的新发展和关键部分。DeepSeek-R1 基于 DeepSeek-V3-Base 模型架构。本文旨在涵盖其设计的所有重要方面。 &#x1f4dd; 1. 输入上下文长度 DeepSeek-R1的输入上下文长…