c++ 中的引用 &

引用与指针经常混淆,总结一下

文章目录

    • [1. 引用与指针的区别](#1. 引用与指针的区别)
    • [2. 引用传递数组](#2. 引用传递数组)
    • [3. 通过引用传递容器和类](#3. 通过引用传递容器和类)
    • [4. 多线程传递容器时用 std:: ref 替代引用传递](#4. 多线程传递容器时用 std:: ref 替代引用传递)

1. 引用与指针的区别

  • 引用(Reference):引用是变量的别名,本质上不是一个变量,而是给一个已经存在的变量起的一个别名。
    • 本质:引用就是变量的别名,不占用新的内存空间。
  • 指针(Pointer):指针是一个变量,存放的是另一个变量的内存地址,需要通过解引用 * 操作符访问该地址的内容。
    • 本质:指针就是存放地址的变量。
cpp 复制代码
int a = 10;
int* p = &a; // p存储的是a的地址
cout << *p;   // 解引用,输出a的值
cpp 复制代码
int a = 10;
int& b = a; // b是a的别名
b = 20;
cout << a; // 输出20

内存结构:

变量 地址 内容
a 0x1000 20
b 0x1000 20
  • b 只是 a 的别名,它们共享同一个内存空间,所以修改 b 就是修改 a。

  • 引用一旦绑定,就不能再改,而指针则不是

cpp 复制代码
int a = 10;
int b = 20;
int& r = a; // r是a的别名
r = b;      // ❌ 不是修改引用,而是给a赋值
  • 引用不占用额外内存,它就是变量的别名。

2. 引用传递数组

  • 通过引用可以传递整个数组,避免数组退化成指针。

数组的数组名(array name)本质上是数组首元素的地址,所以当数组传参时,传递的实际上是数组首元素的地址,而不是整个数组。

cpp 复制代码
void func(int* arr);
void func(int arr[]); // 与前面等价

二者等价,因为数组会自动退化为指针。

例子:通过指针传递一维数组

cpp 复制代码
#include <iostream>
using namespace std;

void modifyArray(int* arr, int size) {
    for(int i = 0; i < size; i++) {
        arr[i] += 10;
    }
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int size = sizeof(numbers) / sizeof(numbers[0]);

    modifyArray(numbers, size);

    for(int i = 0; i < size; i++) {
        cout << numbers[i] << " ";
    }
    return 0;
}
  • 传入的是数组首元素的地址,即 int* arr 接收的是 &numbers[0] 的地址

通过引用传递一维数组

cpp 复制代码
#include <iostream>
using namespace std;

void modifyArray(int (&arr)[5]) {
    for(int i = 0; i < 5; i++) {
        arr[i] += 10;
    }
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    modifyArray(numbers);

    for(int i = 0; i < 5; i++) {
        cout << numbers[i] << " ";
    }
    return 0;
}
  • int (&arr)[5] 表示引用一个长度为5的数组。
  • 传入时,整个数组的内存地址传递过来,并且保留数组大小。

通过指针传递二维数组的例子:

cpp 复制代码
#include <iostream>
using namespace std;

void printArray(int (*arr)[4], int rows) {
    for(int i = 0; i < rows; i++) {
        for(int j = 0; j < 4; j++) {
            cout << arr[i][j] << " ";
        }
        cout << endl;
    }
}

int main() {
    int numbers[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    printArray(numbers, 3);
    return 0;
}

通过引用传递二维数组的例子:

cpp 复制代码
#include <iostream>
using namespace std;

void printArray(int (&arr)[3][4]) {
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 4; j++) {
            cout << arr[i][j] << " ";
        }
        cout << endl;
    }
}

int main() {
    int numbers[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    printArray(numbers);
    return 0;
}

3. 通过引用传递容器和类

如果传递类对象(class),也应该使用引用传递,否则会拷贝整个对象,非常消耗资源。

cpp 复制代码
#include <iostream>
using namespace std;

// ✅ 定义一个 Person 类
class Person {
public:
    string name;
    int age;

    Person(string name, int age) {
        this->name = name;
        this->age = age;
    }

    void display() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};

// ✅ 通过引用传递类对象
void modifyPerson(Person& p) {
    p.age += 10;
    p.name = "Mr. " + p.name;
}

int main() {
    Person person("John", 25);

    // 传递引用,避免拷贝对象
    modifyPerson(person);

    // 输出修改后的信息
    person.display();

    return 0;
}

对于一些容器,可以通过引用传递,避免拷贝且可以修改容器中内容。

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

// ✅ 通过引用传递 vector
void modifyVector(vector<int>& vec) {
    for(int& val : vec) {
        val *= 2;
    }
}

int main() {
    vector<int> numbers = {1, 2, 3, 4, 5};

    // 传递 vector 的引用
    modifyVector(numbers);

    // 打印修改后的 vector
    for(int val : numbers) {
        cout << val << " ";
    }

    return 0;
}

4. 多线程传递容器时用 std:: ref 替代引用传递

C++ STL 容器 (如 vector、list) 在多线程或函数包装中,默认是按值传递。

cpp 复制代码
#include <iostream>
#include <vector>
#include <thread>
using namespace std;

void modifyVector(vector<int>& vec) {
    for (auto& v : vec) {
        v *= 2;
    }
}

int main() {
    vector<int> nums = {1, 2, 3, 4, 5};

    // 将函数和容器传入线程
    thread t(modifyVector, nums);  // ❌ 出问题!
    t.join();

    // 输出结果
    for (auto v : nums) {
        cout << v << " ";
    }

    return 0;
}
  • 虽然 modifyVector 的参数是 vector&,但是 std::thread 默认是按值传递!
  • thread 会拷贝一份 nums,导致无法修改原容器。

C++ 提供了一个工具 std::ref,专门用于"强制引用传递",防止容器被拷贝。

cpp 复制代码
#include <iostream>
#include <vector>
#include <thread>
#include <functional>  // 包含 std::ref
using namespace std;

void modifyVector(vector<int>& vec) {
    for (auto& v : vec) {
        v *= 2;
    }
}

int main() {
    vector<int> nums = {1, 2, 3, 4, 5};

    // ✅ 使用 std::ref,强制引用传递
    thread t(modifyVector, std::ref(nums));
    t.join();

    // 输出结果
    for (auto v : nums) {
        cout << v << " ";
    }

    return 0;
}

在 C++ 中,以下场景:

  • std::thread
  • std::function
  • std::bind
  • std::async
  • std::packaged_task

都默认按值传递参数。如果你传入容器、类、数组、函数等复杂对象,会直接拷贝副本,而不是传引用,需要用 std::ref

相关推荐
Chenyu_31017 分钟前
05.基于 TCP 的远程计算器:从协议设计到高并发实现
linux·网络·c++·vscode·网络协议·tcp/ip·算法
机器视觉知识推荐、就业指导1 小时前
C++ 与 Qt 的内存管理机制
c++·qt
五花肉村长1 小时前
Linux-基础开发工具
linux·运维·服务器·开发语言·c++·visualstudio
点云SLAM2 小时前
C++ 平面拟合原理和最小法实现示例
c++·线性代数·平面·最小二乘法·平面拟合·pca算法
小gpt&2 小时前
01 音视频知识学习(视频)
c++·qt·学习·ffmpeg·音视频
Source.Liu2 小时前
【CXX】6.6 UniquePtr<T> — std::unique_ptr<T>
c++·rust·cxx
仟濹3 小时前
【前缀和与差分 二分搜索 C/C++】洛谷 P1083 借教室
c语言·c++·算法
心态与习惯3 小时前
c++ 调用 gurobi 库,cmake,mac
c++·macos·cmake·第三方库·gurobi
Zԅ(¯ㅂ¯ԅ)3 小时前
计算机图形学交互式技术实验(鼠标、拾取操作和菜单)——绘制可用鼠标进行修改颜色的五角星和矩形
c++·visual studio
John_ToDebug3 小时前
chrome源码中非常巧妙、复杂或者不常见的技术手段
c++·chrome·性能优化