【C++】引用,概念性质,传值传引用 以及 与指针的区别


概念

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

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

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


声明方式

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

代码举例:

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

输出结果:

000000771F79FBD4
000000771F79FBD4

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


引用特性

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

代码举例:

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

输出结果:

000000A00F4FF8E4 000000A00F4FF8E4 000000A00F4FF8E4

常引用

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

声明方式:

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

概念 && 特性

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

代码举例:

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

代码举例:

void modifyValue(int& num) {
    num += 10;
}

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

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

代码举例:

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. 循环遍历容器:使用引用可以避免在循环遍历容器时进行对象的拷贝,提高性能。

代码举例:

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

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

代码举例:

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

传值、传引用的差别

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

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

主要区别:

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

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

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


引用 与 指针 的区别

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

代码举例:

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

输出结果:

num: 5
num after increment: 6

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

引用与指针的不同点:

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值