• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2024-09-19 18:14 Aet 隐藏边栏 |   抢沙发  5 
文章评分 1 次,平均分 5.0

指针

概述

  1. 通常所说的“指针”就是指 指针类型的变量,它保存了 内存中另一个变量的地址
    1. 通过这个指针保存的地址,我们可以 间接访问 指针指向的内存位置的数据
    2. 这个过程称为 解引用(dereferencing

示例

  1. *ptr
  2. 表示 通过指针 ptr 访问其保存的地址所指向的数据
  3. 这个操作叫做 解引用

引用

概述

  1. C++ 中的一个特性,它为变量创建了一个别名,可以通过这个别名直接操作原来的变量
  2. 引用本质上并不像指针那样保存地址,而是直接绑定到一个已存在的变量
    1. 任何对引用的操作都会影响到原始变量

示例

  1. C++ 中,引用是通过在类型后面加上 & 来声明的
    1. 例如 int& ref 表示一个整型引用
    2. 引用必须在定义时初始化,并且一旦绑定到某个变量,就不能再更改引用的对象

局部变量的引用

  1. 一个函数返回 普通局部变量的引用,那么当函数结束后引用的内存将被释放,从而导致 未定义行为
  2. 示例:

  1. 解决:返回静态变量的引用(需谨慎)

  1. 解决:返回堆上分配的内存(动态内存分配)

  1. 解决:返回值传递(复制)

  1. 解决:使用标准库容器(副本)

指针和引用区别

  1. 引用是别名
    1. 引用只是一个现有变量的别名,它和原来的变量是同一个实体
    2. 指针是一个变量,它存储的是另一个变量的地址,需要通过解引用操作符 * 来访问它指向的值
  2. 引用不需要解引用
    1. 使用引用时,可以直接像使用原始变量一样操作,不需要解引用操作符
    2. 指针则需要通过 * 来解引用指向的数据
  3. 引用必须在定义时初始化
    1. 引用在声明时就必须绑定到一个变量,并且绑定后不能再改变引用的对象
    2. 指针可以在任何时候指向不同的变量或地址
  4. 引用不能为空
    1. 引用必须绑定到有效的对象,不能指向 nullptr
    2. 指针可以为空(nullptr),表示它不指向任何东西

左值引用

左值

  1. 指的是内存中有明确存储地址的对象
    1. 简单来说,左值就是可以取地址的对象
  2. 在大多数情况下,等号左边的是左值

定义

  1. 左值引用的语法形式为:T&,其中 T 是类型,& 表示引用

特点

  1. 引用的别名
    1. 左值引用是某个左值的别名
    2. 通过左值引用操作时,实际上是对原对象的操作
  2. 必须绑定到左值
    1. 左值引用只能绑定到左值,即必须是具有确定存储位置的对象或变量
  3. 可以修改引用的值
    1. 通过左值引用可以修改原始对象的值

主要用途

  1. 减少拷贝
    1. 引用可以避免值的拷贝,特别是当传递或返回大型对象时,通过引用传递可以提高效率
  2. 用于函数参数传递
    1. 左值引用常用于函数参数传递,允许函数直接操作传入的对象而不是其副本

  1. 实现操作符重载
    1. 左值引用在实现操作符重载时非常有用,可以用来实现链式操作符(如 a = b = c;)等
  2. 引用作为返回值
    1. 函数可以返回某个对象的左值引用,这样可以允许外部通过引用修改函数内部的对象

右值引用

右值

  1. 指的是无法取地址的临时值,通常是表达式的结果

概述

  1. C++11 引入的一个新特性,用于解决与临时对象和资源管理相关的问题
  2. 它使得程序员能够更高效地操作和管理临时对象
    1. 尤其是在实现移动语义(move semantics)和完美转发(perfect forwarding)时非常有用

定义

  1. 右值引用的声明语法是 T&&,其中 T 是类型,&& 表示右值引用
  2. 右值引用只能绑定到 右值,而左值引用(T&)只能绑定到 左值
  3. 右值引用允许对右值(如临时对象)进行修改
    1. 这使得我们可以避免不必要的拷贝操作,从而提高程序性能

用途

  1. 移动语义
    1. 传统的 C++ 编程中,传递或返回对象通常涉及拷贝操作
      拷贝会产生性能开销,特别是对于大对象
    2. 通过右值引用,我们可以通过移动对象的资源(如动态内存、文件句柄等),而不是拷贝资源,来提高性能
    3. 移动语义允许我们 "偷" 临时对象的资源,而不必拷贝它
  2. 避免不必要的拷贝
    1. 对于临时对象,右值引用可以避免深拷贝,通过移动来减少性能开销
  3. 实现完美转发(Perfect Forwarding
    1. 完美转发是模板编程中的技术,结合右值引用和 std::forward 可以在函数模板中实现高效的参数传递,无论传递的是左值还是右值

移动的本质

  1. 移动 是对资源管理的一种思路
    1. 它的具体实现,由编译支持移动构造函数以及移动赋值操作符等特性
    2. 还需要由程序员在具体的移动构造函数以及移动赋值操作符里面,完成对具体资源的转移工作
  2. 所谓所有权转移
    1. 将原对象的资源(如堆内存的指针)赋值给新对象,并将原对象的资源指针置为 nullptr 或其他无效状态,防止原对象在析构时释放已经转移的资源

对象的引用

  1. 本质上就是 对象的别名

指针的引用

概述

  1. 指针的传递是浅拷贝
    1. 传递一个指针作为函数参数(例如 TreeNode* root),实际上是对指针 进行拷贝
    2. 也就是说,形参 root 是传入的指针的 副本,它保存了和实参相同的地址
    3. 这意味着形参和实参都指向同一个内存地址,是两个不同的指针变量指向同一个内存地址
  2. 使用
    1. 因为形参和实参指向的是同一块内存
    2. 因此在函数内部通过这个指针修改其指向的内存内容,外部是可以看到的
  3. 问题
    1. 如果你在函数内部修改指针本身(即让形参指向不同的地址)
    2. 这个修改只影响形参(即拷贝的副本),不会影响外部的实参指针
  4. 示例
    1. 虽然 root 是指针,但函数内部的 root 只是实参的副本
    2. 修改了形参 root 的指向(让它指向一个新的 TreeNode),但外部的实参 root 并没有改变

指针的引用

  1. 如果希望在函数内部 修改传入的指针(即让外部的指针也指向一个新的对象),那么需要使用指针的引用

关键点

  1. 形参和实参是两个不同的变量
    1. 如果这两个指针变量指向的是同一个地址,那么通过这个地址去修改数据,是可以的
    2. 但是修改保存地址的变量(形参)的内容,并不会同步修改到外面的实参

其他

移动构造函数

  1. 使用 std::movevec 变成右值

  1. 关于移动的本质,见上文

移动赋值操作符

std::move

  1. 它将对象“转换”成右值,使得我们可以对它进行移动操作
  2. std::move 不会移动对象的内容,它只是将对象转换为右值,从而允许移动构造函数或移动赋值操作符来“移动”对象的资源

  1. 不要滥用 std::move
    1. 虽然 std::move 可以将左值转换为右值,但要小心使用它,因为移动之后,原来的对象可能处于无效或不完整的状态

std::forward

  1. 概述
    1. 主要用于实现 完美转发(perfect forwarding
    2. 完美转发允许在模板函数中保持传入参数的“值类别”(即保持参数是左值还是右值的性质),并将其完美地传递给另一个函数
  2. 使用场景
    1. 典型使用场景是 转发参数到另一个函数,尤其是在 函数模板 中
    2. 在函数模板中,std::forward 可以根据模板参数的类型,将左值保持为左值,将右值保持为右值,实现 完美转发
  3. 为什么需要 std::forward
    1. 在模板编程中,我们常常需要将参数传递给另一个函数
    2. 例如,在构造对象或调用其他函数时,你希望根据调用者传入的参数类型,保持其左值或右值属性
    3. 然而,简单的传递参数(不使用 std::forwardstd::move)会导致值类别的丢失——右值会变为左值,无法再高效地使用移动语义

std::forwardstd::move 区别

  1. std::move
    1. 将任何对象转换为右值引用,告诉编译器该对象可以被移动(即使传入的是左值)
  2. std::forward
    1. 根据传入参数的类型和值类别,条件性地将参数转发为左值或右值
    2. 它只在 模板代码中 使用,以保持参数的原始值类别

完美转发

  1. 使用 std::forward 实现完美转发

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享