C++ 学习 ::【基础篇:04】:C++ 引用的概念、特性、常引用、使用方式(建议)及引用与指针

本系列 C++ 相关文章 仅为笔者学习笔记记录,用自己的理解记录学习!C++ 学习系列将分为三个阶段:基础篇、STL 篇、高阶数据结构与算法篇,相关重点内容如下:

  1. 基础篇类与对象(涉及C++的三大特性等);
  2. STL 篇学习使用 C++ 提供的 STL 相关库
  3. 高阶数据结构与算法篇手动实现自己的 STL 库设计实现高阶数据结构,如 B树、B+树、红黑树等。

本期内容:C++ 引用的概念、特性、常引用、使用方式(建议)及引用与指针


目录:
1. 概念、注意点及特性
- - 1.1 概念及注意点
- - 1.2 特性
- - 代码示例及运行结果
2. 一般引用使用场景 / 作用
- - 2.1 作参数(含代码示例)
- - 2.2 作返回值(含代码示例)
- - 2.3 注意点
3. 常引用 const 及权限
- - 3.1 const 的简单回顾
- - 3.2 常引用与权限
4. 关于调用函数引用传参的注意点
5. 引用和指针的区别
- - 5.1 联系
- - 5.2 引用和指针的不同点


合集链接


1. 概念、注意点及特性

1.1 概念及注意点
  1. 引用是给已存在变量取了一个别名;
  2. 引用不是新定义一个变量;
  3. 编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

示例:比如:水浒传中的李逵,在家称为"铁牛",江湖上人称"黑旋风"。“铁牛”、“黑旋风”都是李逵这个人别名(外号),即直白的说就是只同一个人。


  • 基于语法的角度看,引用就是给变量取别名, 使用变量名和别名都是操作同一个“对象”。
1.2 特性
  1. 引用在定义是必须初始化;
  2. 一个变量可以有多个引用;
  3. 引用一旦引用一个实体,就不能在引用其他实体;【区别于指针,指针可以修改指向对象,但是引用不可以】

(实体即引用的对象)


引用的声明定义写法:

  • 类型& 引用变量名(对象名) = 引用实体;【 引用类型必须和引用实体是同种类型的 】
代码示例
#include<iostream>
using std::cout;
using std::endl;

int main() {
    int a = 100;
    int& A = a;			// 定义引用类型:类型& 引用变量名(对象名) = 引用实体;
    /* 声明定义一个 a = 100,数据 100 存储在某块内存区域上,变量名 a 好比这块内存的门牌号名称,引用变量名 A 也是这块内存门牌号名称,无论使用 a / A 都是在使用这块内存区域上的数据! */

    int& aa = a;
    int& AA = A;

    cout << a  << ' ' << &aa<< endl;
    cout << A  << ' ' << &A<< endl;
    cout << aa << ' ' << &aa<< endl;
    cout << AA << ' ' << &AA<< endl;

    /* 结果如下图所示:数据、地址 均相同!!! */
    
    return 0;
}
运行结果

在这里插入图片描述


2. 一般引用使用场景 / 作用

2.1 作参数
  1. 输出型参数:即在函数参数列表中使用形参引用,在函数体内使用形参引用,等同于操作外部的实参!【如代码示例(实现值交换)说明】
  2. 大对象传参提高效率:直白解释:原来一般函数使用的参数返回是值传递的形式,过程中会产生拷贝本!即有一个申请空间并拷贝的小过程;若使用引用,就相当于直接操作原对象,无该过程!在大对象传参过程中有一定的效率提升!
代码示例
#include<iosteam>

/* 以往的指针形式实现! */
void _Swap(int* a, int* b){
    int temp = *a;
    *a = *b;
    *b = temp;
}

/* 引用写法:输出型参数 */
/* 	
	该函数中 a 与 (外部)aaaa 指的就是同一个内存块上的数据,操作 a 等价于操作 aaaa,该形式既是作输出型参数!对于其他数据类型同理。
*/
void Swap(int& a, int& b){		// 此处参数列表中使用引用!
    int temp = a;			
    a = b;
    b = temp;
}

int main(){
    int aaaa = 10, bbbb = 20;
    Swap(aaaa, bbbb);
    
    return 0;
}

2.2 作返回值
  1. 修改返回对象:当引用作为返回值对象时,相当于返回了被操作对象内存块上的对象,你可以直接修改!【如下代码示例】
  2. 大对象传参提高效率。

  • 注意点:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用
    引用返回,如果已经还给系统了,则必须使用传值返回。
代码示例
#include<iostream>
using std::endl;
using std::cout;

struct A { int a[100] = { 0 }; };     // 声明一个结构体

int& test1(A& a) {
    return a.a[0];			// 返回 a[0] 的引用
}

int test2(A& a) {
    return a.a[0];			// 返回 a[0] 的引用
}

int main() {
    A a;
    // 此时 a[0] 应当是:0;
    cout << "a[0]:" << a.a[0] << endl;

    test1(a)++;             // 引用作为返回值
    // test2(a)++;          // 值作为返回值:error:表达式必须是可修改的左值!
    /* 值和引用的作为返回值:
        1. 值作为返回值:返回的值是一个常属性的量,是不可修改的;
        2. 引用作为返回值:个人理解就是返回 a[0] 所在内存块上数据本身,所以可以操作。
    */
    cout << "a[0]:" << a.a[0] << endl;

    return 0;
}
运行结果

在这里插入图片描述


关于注意点的说明:使用引用作为返回值的前提是:被掉函数销毁后,操作的对象依然存在【否则会出问题】:如上代码中,我们使用的引用作为参数,就是操作原结构体 A,调用函数后,函数栈帧销毁了,也不影响原结构体 A【该函数的功能就相当于给 a[0] 取一个别名】。


2.3 注意点

任何时候都可以用引用作参数,但是不是任何时候都可以用引用作返回值!!!

对比引用与指针:引用相当于对指针的"改进",弱化了对指针这个高深内容的操作难度!


3. 常引用 const 及权限

3.1 const 的简单回顾

const 的作用:声明被修饰对象为:只可读变量;

说白了:被const 修饰的变量不可以修改!

int a = 10;		// 对于 变量 a 而言,我们可读可写
a = 20;			// 可写:即修改

const int b = 100;	// 对于 变量 b 而言,我们只可读不可写!
// b = 200;			// 错误

在这里插入图片描述


3.2 常引用与权限

1. 变量的操作权限:只能平移或缩小,不能放大!!!

2. 权限的放大缩小限制只针对于引用!

代码示例说明
#include<iostream>


int main(){
    
    int a = 10;			// 操作权限:可读可写
    const int b = 20;	// 操作权限:只读
    
    // 引入引用
    int& aa = a;		// 操作权限:可读可写【该情形是:权限平移】
    
    // int& c = b;			// 错误:声明的引用是 int 类型且可读可写
    /* 此处权限:相当于放大了:只读 => 可读可写!	错误!!! */
    
    /* 正确用法 */
    const int& c = b;
    /* b、c 均只可读! */
    // b = 2000;			错误!
    // c = 2000;			错误!
    
    /* 注意! */
    const int& d = a;		// 正确!
    /* a:可读可写;d:只读:该过程相当于权限减小,后续会有十分重要的作用! */
    
    return 0;
}

4. 关于调用函数引用传参的注意点

使用建议:如果使用引用传参,若在函数内不进行值修改操作,建议使用 const 引用传参,如下 func2 和 func3

代码示例
#include<iostream>

using std::cout;
using std::endl;

void func1(int n){
    cout << n << endl;
}

void func2(int& n){
    cout << n << endl;
}

void func3(const int& n){		// 如函数内不修改值,推荐该形式写法
    cout << n << endl;
}

int main(){
    int aa = 10;
    const int bb = 20;
    
    /* 对于 func1 而言参数列表是简单的形参形式,就是简单的拷贝式值转递,故三种情形都能通过 */
    func1(aa);			// 实参 aa 可读可写
    func1(bb);			// 实参 bb 只读
    func1(30);			// 常量 30 只读
    
    /* 对于 func2 而言参数列表是引用形式的形参,本身是 int 类型且可读可写 */
    func2(aa);			// 实参 aa 可读可写
    // func2(bb);		// 实参 bb 只读:传递失败!
    /* 编译器提示:E0433:将 "int &" 类型的引用绑定到 "const int" 类型的初始值设定项时,限定符被丢弃	*/
    func2(30);			// 常量 30 只读
    /* 编译器提示:E0461:非常量引用的初始值必须为左值 */
    
    return 0;
}

5. 引用和指针的区别

面试回答角度:使用场景、语法特性及底层原理

5.1 联系

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

在底层实现上实际是有空间的,因为引用是按照指针方式来实现的(看汇编代码)。

用途基本相似。

5.2 引用和指针的不同点

1. 引用概念上定义一个变量的别名,指针存储一个变量地址。

2. 引用在定义时必须初始化,指针没有要求。

3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。

4. 没有 NULL 引用,但有 NULL 指针。

5. 在 sizeof 中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
位平台下占4个字节)。

6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。

7. 有多级指针,但是没有多级引用。

8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理。

9. 引用比指针使用起来相对更安全。


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NPC的白话文谈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值