【基础知识】引用是什么?

1 为什么会出现引用?

在生活中,我们可能会给一些同学起外号,以“吴磊”同学为例,我们可以叫他“吴三石”,当我们叫到这个外号的时候就自然而然地想到“吴磊”同学,“吴三石”就是他的别名,而引用也可以这样简单理解:在语法层面上,引用就是取别名

引用的出现是为了解决一些问题,主要包括:

● 避免拷贝构造函数创建临时对象所带来的开销。当将一个对象作为函数参数传递或返回时,如果使用传值方式,则会调用拷贝构造函数创建一个临时对象,这会带来额外的开销,而使用引用则可以避免这种开销。

● 让我们在函数内部修改传入的对象。如果不使用引用,则只能通过指针来实现这一点。

2 引用是什么?

在 C++ 中,引用是一种复合类型,它是一个对象的别名。引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共有一块内存空间

语法格式:    类型 & 引用变量名(对象名) = 引用实体

例子:

int main(){
    //引用:取别名
    int a = 10;
    int& b = a;//定义引用类型
    int& c = b;
    return 0;
}

本段代码中,a 变量取了 b,c,可以通过调式观察它们的内存。

通过调取内存可以发现,a、b、c 所指向的是同一块内存空间。

注意:

引用类型必须和引用实体同种类型

3 引用的特性

引用的三个特性:

1、引用变量创建必须初始化

由于引用是对已经存在的变量进行取别名,因此使用引用时,必须指定变量(初始化)
eg:    
    int &d;  //错误,为初始化

2、引用变量不能赋值为空,必须指向一个房子

3、引用变量不可以改变指向,不能修改引用关系

因为引用一旦引用了一个已经存在的实体,就是这个实体的别名,当然不能再成为其他实体的别名。

4 常引用

声明方式:

const 类型标识符 &引用名 = 目标变量名

用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为 const ,达到了引用的安全性.

int main(){
    //1.
    const int x = 20;
    int& y = x;
    return 0;
}

当我们编译这段代码发现编译器报出错误警告:无法从“const int”转换为 “int &”

这是因为我们在引用的时候要遵守引用的规则:

引用规则:    对原变量的引用,权限不能放大

这段代码中 x 变量是 const 修饰的一个常变量,只有可读权限。而我们引用的类型是 int,不仅有可读权限,还有可修改权限。这就造成了对原变量的权限放大。根据我们引用原则知道,对原变量的引用,权限是不能放大的,这就是为什么这段代码会报错的原因。

那我们再来看这一段代码,他能否编译成功?

int main()
{
    //2.
    const int x = 20;
    const int& y = x;//不变
 
    //3.
    int c = 30;
    const int& d = c;//缩小
 
    return 0;
}

这段代码我们发现编译成功了,我们也可以轻松地分析出这里的引用是遵守引用规则的,我们发现,权限不变或者权限缩小都是符合规则的,唯一需要注意的是:权限不能放大

5 引用的使用场景

1、函数参数传递:

在函数定义中,可以使用引用作为参数来传递变量的值,避免复制大型对象或数组,提高程序的执行效率。

引用可以作为函数的参数进行传递,这样可以避免复制参数值而浪费空间和时间。同时,函数内对引用参数的修改也可以影响到函数外的实际参数。

void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5, y = 10;
    swap(x, y);
    cout << "x = " << x << ", y = " << y << endl;
    return 0;
}

2、函数返回值

在函数中,可以使用引用作为返回值类型,避免返回大型对象或数组的副本,提高程序的执行效率。

引用可以作为函数的返回值,这样可以避免返回大量数据而浪费空间和时间。同时,函数返回的引用也可以用于左值赋值操作,从而改变原始数据的值。

int& max(int& a, int& b) {
    return (a > b) ? a : b;
}

int main() {
    int x = 5, y = 10;
    max(x, y) = 20;
    cout << "x = " << x << ", y = " << y << endl;
    return 0;
}

3、遍历数组

在遍历数组时,可以使用引用来避免对数组元素的复制。

int arr[5] = {1, 2, 3, 4, 5};
for (int& x : arr) {
    x *= 2;
}

4、类成员变量

在类中,可以使用引用类型的成员变量来避免复制对象的副本,节省内存,提高程序的执行效率。

class Vector {
public:
    Vector(int size) : size(size), data(new int[size]) {}
    int& operator[](int index) {
        return data[index];
    }
private:
    int size;
    int* data;
};

int main() {
    Vector v(5);
    for (int i = 0; i < 5; i++) {
        v[i] = i + 1;
    }
    for (int i = 0; i < 5; i++) {
        cout << v[i] << " ";
    }
    cout << endl;
    return 0;
}

5、操作符重载

在 C++ 中,可以使用引用来实现操作符重载。例如,可以重载“=”操作符,使用引用来实现对象之间的赋值操作。

class MyString {
public:
    MyString& operator=(const MyString& other) {
        if (this != &other) {
            delete[] data;
            size = other.size;
            data = new char[size];
            memcpy(data, other.data, size);
        }
        return *this;
    }
private:
    int size;
    char* data;
};

6 引用真的不占内存空间吗?

在 C++ 中,引用本身不会占用额外的内存空间,因为引用本质上只是一个别名或者说是指针的另一种表现形式。换句话说,当我们声明一个引用变量时,它只是为原变量起了一个别名,引用变量和原变量指向同一个内存地址,因此引用变量不需要分配额外的内存空间。

例如,以下代码中的 ”&ref“ 就是一个引用变量,它不会分配任何内存空间:

int x = 10;
int& ref = x;

然而,需要注意的是,对于一些内置类型以外的复合类型,如类或结构体,其引用的行为可能于内置类型有所不同。例如,如果我们在类中声明一个引用成员变量,那么这个引用变量实际上会占用一定的内存空间,因为引用本身需要在类中分配 空间。不过这种情况并不常见。

引用的底层实现通常是通过指针来实现的,而指针是需要占用内存空间的,因此,当我们使用引用时,实际上是在使用底层指针所指向的对象,而不是引用本身。在这种情况下,所占用的内存空间取决于所引用的对象的类型和 大小,而不是引用本身。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值