【C++】引用 的概念性质 与 比较

文章目录


概念

C++中引入了一个新的概念:引用

是一种特殊的数据类型,用于给变量起一个别名。通过引用,我们可以使用一个变量来访问另一个已存在的变量,从而实现对同一块内存空间的多个名称的访问。

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间


声明方式

数据类型 &引用变量名 = 原变量名;

代码举例:

dart 复制代码
void TestRef1()
{
	int a = 10;
	int& ra = a;//<====定义引用类型
	printf("%p\n", &a);
	printf("%p\n", &ra);
}

输出结果:

dart 复制代码
000000771F79FBD4
000000771F79FBD4

需要注意的是:引用类型必须和引用实体是同种类型的


引用特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用 一个实体,再不能引用其他实体

代码举例:

dart 复制代码
void TestRef2()
{
	int a = 10;
	// int& ra; // 该条语句编译时会出错
	int& ra = a;
	int& rra = a;
	printf("%p %p %p\n", &a, &ra, &rra);
}

输出结果:

dart 复制代码
000000A00F4FF8E4 000000A00F4FF8E4 000000A00F4FF8E4

常引用

常引用(常量引用)是指在声明引用时使用const关键字来限制被引用对象的修改。通过常引用,我们可以确保在引用的生命周期内,被引用的对象不会被修改。

声明方式:

常引用的声明方式为:const 数据类型 &引用变量名 = 原变量名;

概念 && 特性

  1. 常引用绑定到了一个常量或者表达式上,它们不能被修改 ,因为常引用指向的是一个只读的对象
  2. 常引用可以绑定到非常量对象常量对象 以及表达式上,但不能通过常引用来修改所引用的对象。
  3. 常引用可以用于函数参数,从而避免了函数内部对被引用对象的不必要拷贝。

代码举例:

dart 复制代码
#include <iostream>

void print(const int& num) {
    std::cout << "Value: " << num << std::endl;
}

int main() {
    int x = 5;
    const int& ref = x; // 声明一个常引用ref绑定到变量x

    std::cout << "x = " << x << std::endl; // 输出:x = 5
    std::cout << "ref = " << ref << std::endl; // 输出:ref = 5

    // ref = 10; // 错误!常引用无法修改被引用的对象

    x = 10; // 修改原始变量x的值

    std::cout << "x = " << x << std::endl; // 输出:x = 10
    std::cout << "ref = " << ref << std::endl; // 输出:ref = 10

    int y = 20;

    print(y); // 传递y的常引用给函数print

    return 0;
}

应用场景

  1. 函数参数传递:通过引用作为函数参数,可以避免不必要的对象拷贝,提高性能,并且可以修改原始数据。特别是对于大型对象或容器,使用引用作为函数参数通常更高效。

代码举例:

dart 复制代码
void modifyValue(int& num) {
    num += 10;
}

int main() {
    int x = 5;
    modifyValue(x); // 通过引用修改原始变量x的值

    return 0;
}
  1. 做返回值:通过将函数返回类型声明为引用,可以避免产生临时对象,并且可以直接操作原始数据。

代码举例:

dart 复制代码
const string& getFullName(const string& firstName, const string& lastName) {
    static string fullName;
    fullName = firstName + " " + lastName;
    return fullName;
}

int main() {
    const string& name = getFullName("Jonathan", "Jostar"); // 引用函数返回值,避免临时对象的创建
    return 0;
}
  1. 循环遍历容器:使用引用可以避免在循环遍历容器时进行对象的拷贝,提高性能。

代码举例:

dart 复制代码
std::vector<int> numbers = {1, 2, 3, 4, 5};`在这里插入代码片`

for (const auto& num : numbers) {
    std::cout << num << " "; // 使用引用遍历容器,避免拷贝
}
  1. 类成员变量:引用可以作为类成员变量的别名,方便访问和修改成员变量。

代码举例:

dart 复制代码
class Circle {
public:
    Circle(float& r) : radius(r) {}

    void doubleRadius() {
        radius *= 2;
    }

private:
    float& radius;
};

int main() {
    float r = 5.0;
    Circle circle(r); // 引用作为类成员变量的别名,方便访问和修改

    circle.doubleRadius(); // 直接修改原始数据r

    return 0;
}

传值、传引用的差别

传值是指将变量的值复制一份,然后将复制的值传递给函数或方法。在函数或方法内对参数的修改不会影响原始变量的值。

传引用是指将变量的引用(内存地址)传递给函数或方法。在函数或方法内对参数的修改会直接影响原始变量的值。

主要区别:

内存使用:传值需要额外的内存空间来存储复制的参数值,而传引用不需要额外的内存空间。

参数的修改:传值不会修改原始变量的值,而传引用可以修改原始变量的值。

效率:传值涉及数据的复制,可能会产生额外的性能开销,特别是当传递大型对象时。传引用不需要进行数据复制,因此更高效。


引用 与 指针 的区别

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

代码举例:

dart 复制代码
void increment(int& ref) {
    ref++;
}

int main() {
    int num = 5;
    int& ref = num; // 引用ref和变量num共享同一块内存空间

    std::cout << "num: " << num << std::endl; // 输出 num 的初始值
    increment(ref); // 通过引用修改变量num的值
    std::cout << "num after increment: " << num << std::endl; // 输出增加后的num的值

    return 0;
}

输出结果:

dart 复制代码
num: 5
num after increment: 6

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

引用与指针的不同点:

  1. 引用 概念上定义一个变量的别名 ,指针 存储一个变量地址
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而 指针 可以在任何时候指向任何一个同类型实体
  4. 没有空引用,但 有空指针
  5. sizeof中含义不同 :引用结果为引用类型的大小 ,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  6. 引用自加即引用的实体增加1 ,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用 ,引用编译器自己处理
  9. 引用 比指针使用起来相对更安全

总的来说,引用 提供了一种更安全、更简洁的方式来访问对象,它是指针的一种高级封装指针则提供了更灵活的内存管理和操作方式。在选择使用引用还是指针时,需要根据具体的需求和语言特性进行选择。

相关推荐
wjs2024几秒前
MongoDB 更新集合名
开发语言
monkey_meng4 分钟前
【遵守孤儿规则的External trait pattern】
开发语言·后端·rust
一只小小汤圆8 分钟前
opencascade源码学习之BRepOffsetAPI包 -BRepOffsetAPI_DraftAngle
c++·学习·opencascade
legend_jz29 分钟前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
嘿BRE39 分钟前
【C++】几个基本容器的模拟实现(string,vector,list,stack,queue,priority_queue)
c++
tangliang_cn1 小时前
java入门 自定义springboot starter
java·开发语言·spring boot
程序猿阿伟1 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
新知图书1 小时前
Rust编程与项目实战-模块std::thread(之一)
开发语言·后端·rust
威威猫的栗子1 小时前
Python Turtle召唤童年:喜羊羊与灰太狼之懒羊羊绘画
开发语言·python
力透键背1 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript