c++引用

本文详细介绍了C++中的引用概念、使用场景及其与指针的区别,包括引用在函数参数传递、返回值、赋值操作中的优势,以及左值引用、右值引用和指向指针的引用的特性和应用。
摘要由CSDN通过智能技术生成

引用的概念:

C++中引用是一个变量或对象的别名,使用&符号来创建一个引用变量。

引用必须在定义时初始化,相比指针用法更加直观和简单。

由于不需要将参数值从内存中拷贝一份,使用引用传递参数还可以减轻函数开销。

引用的作用:

1. 对函数传参进行简化:引用可以避免在函数传参时发生数值拷贝,使用引用传递参数还可以减轻函数开销,提高程序的运行效率。因此,通常使用引用来传递大的数据结构或者类对象。

2. 用于返回值或者赋值时,可以使语句更加简洁:

在函数返回时,将变量的引用作为返回值,可以避免在内存中创建返回值的副本,提高程序效率。

此外,对于类对象的赋值操作,也可以通过重载等号运算符返回对象的引用变量,使代码更加简洁易懂。

3. 在重载运算符时,可以更加方便地对对象进行操作。

#include <iostream>

using namespace std;

int main()
{
    int num = 10;
    int &dd = num;
    int *ptr = &dd;
    // 引用就是变量的别名,等价于被引用的变量,引用本身不占用地址空间
    // int *ptr = &dd; 等价于int *ptr = &num;
    cout << "*ptr = " << *ptr << endl;
    cout << dd << endl;
    // 通过引用修改变量的值
    dd = 20;
    cout << "dd = " << dd << " num = " <<num << endl;
    // 通过指针修改变量的值
    *ptr = 30;
    cout << "*ptr = " << *ptr << " num = " << num << endl;

    return 0;
}

引用和指针的区别:

C++中的引用和指针都是用来访问变量的地址的。它们可以实现类似于传递参数和访问数组等功能。但是引用和指针的呈现方式有所不同,下面做一些比较:

1. 定义方式:

引用和指针都是在定义时通过符号来实现的,但是定义时符号的不同,使两者在定义方式上有所区别。

引用的定义:

int a = 1;

int &b = a; // 用&符号定义,b是a的引用,b是a的别名

指针的定义:

int a = 1;

int *p = &a; // 用*符号定义,p是a的指针

2. 是否需要初始化:

在定义时,引用必须进行初始化,指针则可以先定义再初始化。

3. 是否可变:

引用在被初始化后就不能再指向别的变量,即引用指向的目标不能再变。指针可以根据需要更改指向的对象。

4. 是否有空指针:

引用不能是空的,指针可以是空的,即指针可以被赋值为nullptr或者NULL。

5. 进行运算:

指针可以进行运算,比如加、减、比较等运算。而引用不存在运算符来处理地址的值。

6. 是否可以有多级:

指针可以存在多级指针,即一个指针可以指向另一个指针,以及更多的级别嵌套。

而引用只能是一级别,即一个引用只可以是一个变量的别名。

7. 不同类型的指针:

指针可以指向不同类型的对象,如int、char、double等等不一样的数据类型。

而引用只能指向跟自己数据类型相同的对象。

两者的相同点:

引用和指针都是用来间接访问变量的地址。它们都可以当做函数参数传递,以及实现对数组等数据类型的操作。指针在更复杂的而且更高级的应用场景中发挥着不可替代的作用。而引用则更加简单,在一些场景中使用更加便捷。

通过引用传递参数:

#include <iostream>
using namespace std;

// 指针传参
void SwapByPoint(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

// 引用传参
void SwapByQuote(int &a, int &b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

// 引用传参
void SetValue(int &a)
{
    a += 1;
}

void SetPtr(int *&ptr)
{
    int a = 10;
    ptr = &a;
}

int main()
{
    int a = 3, b = 4;
    // 指针传参
    SwapByPoint(&a, &b);
    cout << "指针传参: a = " << a << " b = " << b << endl;
    // 引用传参
    int &aa = a;
    int &bb = b;
    SwapByQuote(aa, bb);
    cout << "引用传参: a = " << a << " b = " << b << endl;
    SetValue(aa);
    SetValue(bb);
    cout << "引用传参: a = " << a << " b = " << b << endl;
    // 引用传参,传递指针类型的引用
    int *ptr = NULL;
    int *&p = ptr;
    SetPtr(p);
    if (p == NULL) {
        cout << "p == NULL" << endl;
    } else {
        cout << "p is not null " << endl;
    }

    return 0;
}

左值引用与右值引用:

C++11引入了右值引用的概念,将引用分为左值引用(Lvalue Reference)和右值引用(Rvalue Reference)两种。左值引用即常规的引用,而右值引用是新引入的一种引用方式,用于支持C++11中的移动构造函数和移动赋值运算符,以及实现完美转发等功能。

1. 左值引用

左值引用是指对某个变量或者内存空间的引用,在代码中出现的变量是左值,其地址可以被获取,

可以使用&符号进行获取。因此,左值可以被修改或者取地址,是可被取地址的表达式。

int a = 1;

int &b = a; // 这里b是a的左值引用,它可以被取地址,是一个可修改的变量。

2. 右值引用

右值引用是指对内存中某个临时数据的引用,也就是没有持久性的数据,即将要被销毁的数据。

右值引用用&&符号进行表示,只能转移值,并不能修改原值,是不可被取地址的表达式。

int &&c = 10; // 这里c是10的右值引用,它不能被取地址,是一个不可修改的变量。

需要注意的是,右值引用通常用于移动构造函数和移动赋值运算符等操作,以及实现完美转发等任务。在移动赋值时,右值将被转换为左值引用,可以进行赋值操作。

下面是一个移动构造函数的例子:

在这个例子中,String中的移动构造函数参数为string的右值引用,当用右值构造string实参时,

会调用移动构造函数,并将该右值移动到成员变量_str中。


#include <iostream>
#include <string>
using namespace std;

class String {
public:
    // 移动构造函数
    String(string&& rhs) {
        // `move` 函数是 C++11 中新增的一个函数,作用是将一个左值(可以被取地址的对象)强制转换为右值引用。
        // 其实现是通过移动语义实现的。在进行对象拷贝或者赋值操作时,如果想要使用其移动语义,
        // 可以使用 `std::move()` 函数对该对象进行转移操作,将其强制转换为右值引用。
        // 在移动(靠赋值或构造函数)大型对象时,使用移动语义可以显著提高程序的运行效率。
        _str = move(rhs); // 将rhs中的成员变量移动到*this中
        cout << _str << endl;
    }

private:
    string _str;
};

int main() {
    // 这段代码使用了移动构造函数将一个右值 `string` 转移给了 `String` 对象中的 `_str` 成员变量
    // 在移动构造函数中,成员变量 `_str` 通过 `std::move(rhs)` 获取到一个右值引用,
    // 其实质是将右值对象 `rhs` 中的 `_str` 强制转换为右值引用并移动给了成员变量 `_str`。
    // 这里的 `rhs` 是一个右值引用参数,其本身是一个左值,需要使用 `std::move` 函数将其内部实现从左值转换为右值引用,以便进行移动操作。
    // 在移动时注意保持被移动对象的内容处于一个合法状态,以避免资源的泄漏等问题。
    // 使用移动语义的主要优点是,可以避免许多多余的数据拷贝,从而提升程序的执行速度。
    // 特别是在处理大量大数据量的情况下,使用移动语义可以显著提高程序的性能。
    String str(string("Hello World!")); // 用右值构造string,然后移动它
    return 0;
}

指向指针的引用(Pointer-To-Pointer Reference)是 C++ 中一种比较特殊的引用数据类型,

它不仅可以用于传递指针,同时还可以用于修改指针本身。它强调了指向指针本身的对指针的访问,指向指针的引用将本身作为指针的别名,同时指向指针的引用也被称为指针引用(Pointer Reference),它关注指针变量的地址,而不是它所指向的变量和值。

将指针更改为指向另一个变量的地址也会影响原始指针的指向。

定义指向指针的引用时,需要在数据类型前加一次“&”,这样就可以定义一个指向指针的引用。

int a = 10;

int* p = &a; // 定义指向指针的引用

int** pp = &p; // 定义一个指向指针的指针

int*& ref_ptr = p;  // p 的别名,定义一个指向指针的引用

由于指向指针的引用是指针的别名,因此定义指针引用时,需要在变量名前加上“&”符号。

#include <iostream>
using namespace std;
// 使用指向指针的引用时,可以将其作为函数的参数类型,以便在函数内部可以修改指针本身。
// 定义函数 SetPtr,接受一个指向指针的引用参数
void SetPtr(int *&ptr, int value)
{
    ptr = new int(value); // 修改指针本身的值
}
// 在这个例子中,定义了一个函数 `SetPtr`接受一个指向指针的引用参数 `int* &ptr`,
// 并在函数内部分配了一块内存,修改指针本身的值。
// 在调用该函数时,我们将指针变量 `p` 和一个整数 `10` 作为参数传递给函数,
// 函数将修改指针本身的值,可以在函数外部访问这块内存地址。
int main()
{
    int *point = nullptr;
    SetPtr(point, 10);
    cout << "point = " << *point << endl;

    return 0;
}

使用指向指针的引用也可以方便的在复杂数据结构中操作指针,指向指针的引用通常用于多级指针的操作。

#include <iostream>
using namespace std;

typedef struct tag_s {
    int a;
    int *p;
    int **pp;
} S;

int main()
{
    S data;
    int value = 10;
    data.a = 100;
    data.p = &value;
    data.pp = &(data.p);

    int *&ref_p = data.p; // ref_p是p的别名
    cout << *(data.p) << endl; // 输出10
    cout << *(ref_p) << endl; // 输出10
    cout << *(*(data.pp)) << endl; // 输出10
    return 0;
}
// 在上面的例子中,我们定义了一个结构体,其中包含了一个整型变量、一个指针和一个指向指针的指针。
// 当我们需要访问结构体中的“pp”成员变量时,就可以使用指向指针的引用,通过 `int**& ref_pp = data.pp;` 
// 定义一个指向指针的引用,便于我们在函数内部修改指针本身的值。
// 通过对指向指针的引用的使用,可以比较方便的在复杂的数据结构中操纵指针。
// 需要注意的是,指向指针的引用通常用于多级指针的操作,语法比较复杂,需要注意使用的正确性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值