当前位置: 首页 > article >正文

引用 CPP

引用的语法

在C++中,声明一个引用需要使用&符号。例如,如果有一个整数变量x,可以这样声明一个引用:

int x = 10;
int& ref = x;

这里,refx的引用。这一点非常直观。

引用的特性

引用一旦被初始化,就不能再指向其他变量。这与指针不同,指针可以在其生命周期内重新指向不同的变量。此外,引用必须在声明时被初始化,而指针则不一定。

引用的本质

是什么呢?从根本上说,引用是一个别名,它直接引用它所绑定的变量的内存地址。因此,通过引用进行的任何操作实际上都是对原始变量的操作。这使得引用在性能上非常高效,因为不需要通过指针进行解引用操作。

引用作为函数参数

传递引用可以避免复制整个对象,从而提高效率,特别是对于大型对象。例如:

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

在这个例子中,ab是按引用传递的,因此在swap函数内部对它们的修改会影响实际的参数。

但是,如果想让引用只读,以防止函数修改原始变量,这时就用到了常量引用。其语法如下:

void print(const int& value) {
    std::cout << value << std::endl;
}

这里,value是按常量引用传递的,因此函数内部无法修改value

指针引用

这部分可能会有些复杂。在C++中,引用本身不是指针,但它们的行为有时相似。指针可以为空,可以指向不同的变量,而引用则不能。此外,指针需要使用 * 和 -> 作符进行解引用,而引用则直接使用。

让我们通过一个例子来澄清引用和指针的区别

int x = 10;
int y = 20;

int& ref = x; // 引用
int* ptr = &x; // 指针

ref = y; // 现在ref引用y
ptr = &y; // ptr现在指向y

std::cout << ref << std::endl; // 输出20
std::cout << *ptr << std::endl; // 也输出20

在这个例子中,引用和指针都可以重新指向另一个变量,但引用在声明后不能被重新赋值,而指针可以。

实际上,一旦引用被初始化,它就永远不能指向另一个变量。因此,ref = y;是错误的,因为它试图使ref引用另一个变量,这是不允许的。正确的做法是通过引用修改y的值:

int x = 10;
int y = 20;

int& ref = y; // 现在ref引用y
ref = 30; // 现在y是30

std::cout << y << std::endl; // 输出30

这样更有道理。引用在声明时绑定到一个变量,并且不能在之后重新绑定,而指针可以在其生命周期内重新指向不同的变量。

总结一下: 

  • ​引用的语法: int& ref = x;
  • 特性: 必须在声明时初始化,不能重新绑定。
  • 本质: 变量的别名,直接引用其内存地址。
  • 作为函数参数: 通过引用传递以提高效率。
  • 常量引用: 使用 const 确保函数不会修改原始变量。
  • 指针引用: 与指针相似,但不能重新指向不同的变量。​​

左值引用和右值引用

     首先,什么是左值右值

        在C++中,左值是指可以出现在赋值语句左边的表达式,即具有持久存储期的对象,可以被引用。右值则是指出现在赋值语句右边的表达式,通常是一个临时对象或字面量。

然而,从C++11开始,引入了右值引用,这改变了游戏规则。右值引用允许我们引用右值,这对于实现移动语义和完美转发等特性至关重要。

左值引用

开始。左值引用,如前所述,是绑定到左值的引用。它们通常用于引用具有持久存储期的对象。例如:

int x = 10;
int& ref = x; // 左值引用

这里,refx 的左值引用。由于 x 是一个持久对象,ref 可以安全地引用它。

右值引用

现在,右值引用是C++11中引入的一个新概念。它们允许我们引用右值,即那些没有持久存储期且通常在表达式末尾被销毁的临时对象。右值引用的语法使用两个 & 符号:

int y = 20;
int&& rref = std::move(y); // 右值引用

在这个例子中,y 是一个左值,但通过 std::move,我们将其转换为右值,rrefy 的右值引用

移动语义

但是,为什么我们需要右值引用呢?这主要是为了实现移动语义。在没有右值引用的情况下,当一个函数需要处理临时对象时,通常会进行复制,这可能会非常耗时,特别是对于大型对象。通过使用右值引用,我们可以“移动”资源,而不是复制它们,从而显著提高性能。

让我们通过一个简单的例子来说明这一点。假设我们有一个 std::vector,我们希望将其内容移动到另一个 vector 中:

std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = std::move(vec1); // 移动 vec1 到 vec2

在这里,vec1 被移动到 vec2,而不是复制。vec1 现在处于一个未定义状态,但 vec2 拥有 vec1 的所有资源。这比复制整个 vector 更高效。

完美转发

另一个重要的用途是完美转发,这允许函数将参数原样传递给其他函数,而不会进行不必要的复制。例如:

template <typename T>
void forward(T&& arg) {
    // 将 T&& 作为右值引用转发
}

int main() {
    int a = 10;
    forward(a); // a 作为左值传递
    forward(20); // 20 作为右值传递
}

在这个例子中,forward 函数可以接收左值和右值,并将它们原样转发,而不会进行不必要的复制。

但是,使用右值引用时需要小心。由于右值引用可以引用那些即将被销毁的临时对象,我们必须确保在引用它们时这些对象仍然有效。否则,我们可能会遇到未定义行为。

让我总结一下:

  • 左值引用:引用具有持久存储期的对象,不能引用临时对象。

  • 右值引用:从C++11开始引入,允许引用临时对象,这对于实现移动语义和完美转发至关重要。

  • 左值:可以出现在赋值语句左边的表达式,具有持久存储期。

  • 右值:出现在赋值语句右边的表达式,通常是临时对象或字面量。

为了确保理解正确,让我们再看一个例子。假设我们有一个函数,它接受一个右值引用:

void takeOwnership(std::vector<int>&& vec) {
    // 拥有 vec 的所有权
}

int main() {
    std::vector<int> vec = {1, 2, 3};
    takeOwnership(std::move(vec)); // 将 vec 的所有权移动到 takeOwnership
}

在这个例子中,takeOwnership 函数通过右值引用接收 vec,这意味着 vec 的内容被移动,而不是复制。这使得 takeOwnership 可以高效地拥有 vec 的资源。

​左值引用: 引用具有持久存储期的对象。右值引用: 允许引用临时对象,支持移动语义和完美转发。左值: 可以出现在赋值语句左边的表达式。右值: 出现在赋值语句右边的表达式,通常是临时对象或字面量。​​


http://www.kler.cn/a/506930.html

相关文章:

  • Go Ebiten小游戏开发:贪吃蛇
  • HunyuanVideo 文生视频模型实践
  • Maven 配置本地仓库
  • 【Vim Masterclass 笔记14】S07L29 + L30:练习课08 —— Vim 文本对象同步练习(含点评课内容)
  • 下载文件,浏览器阻止不安全下载
  • C语言:数据的存储
  • Chapter1:初见C#
  • C# 并发和并行的区别--16
  • JavaScript分页的制作
  • 【WRF-Urban】新增静态输入数据:URB_PARAM、FRC_URB2D、AHE等
  • redis性能优化参考——筑梦之路
  • 复古怀旧美感35mm胶片模拟色调颗粒汽车商业摄影Lightroom调色预设 Mike Crawat 2024 35MM FILM LOOK PRESETS
  • polarDB报错column reference is ambiguous
  • 手机壁纸设计技巧:打造个性化视觉盛宴
  • zustand 切片模式使用,persist 中间件持久化状态
  • 使用 Kubernetes 实现负载均衡
  • 初步认识 Neo4j 图数据库
  • 《零基础Go语言算法实战》【题目 4-3】请用 Go 语言编写一个验证栈序列是否为空的算法
  • Linux(CentOS/CTyunOS)缺少GBK、GB2312中文字符集
  • 【设计模式-结构型】代理模式
  • RK3568笔记七十四:AP配网实现
  • VLAN是什么?有什么作用?
  • 高效构建与部署Python Web应用:FastAPI的测试与持续集成
  • 基于单片机的智能输液系统
  • C#中委托和函数类的关系
  • 11-2.Android 项目结构 - themes.xml 文件基础解读