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

C++移动语义

一. 浅拷贝与深拷贝

struct Node{ int size; int *p; Node() = default; Node(int x){ size = x; p = new int(size); } };

1. 浅拷贝

按位拷贝对象,如果拷贝对象中存在指针,那么两指针指向的地址相同(但是两指针的地址不一定相同)

Node shllowCopy(Node &v){ this -> size = v.size; this -> p = v.p; }

2. 深拷贝

拷贝对象的所有属性,如果拷贝对象中存在指针,那么会在堆上重新开辟一片相同大小的空间,并让指针指向该空间

Node deepCopy(Node &v){ this -> size = v.size; this -> p = new int[size]; for(int i = 0; i < size; i++){ this -> p[i] = v.p[i]; } }

二. 左值与右值

  • 左值(lvalue):表达式结束后仍然存在的持久对象
  • 右值(rvalue):表达式结束后就被销毁的临时对象

一般来说,有变量名的都是右值,没有变量名的是左值

特殊:字符字面量(如'c','a')这种是右值;但是字符串字面量,如"abcd"这种,由于它存在于静态存储区,所以字符串字面量均为左值

三. 左值引用与右值引用

1. 左值引用(&)

左值引用本质上是给左值变量起了一个别名

  • 改变任意一方,双方都改变
  • 只能绑定在左值上
  • 特殊:const int &x = 10,为常量左值引用,可以绑定右值,但是不可更改

2. 右值引用(&&)

绑定在右值上,可以延长临时对象的生命周期,并允许更改

应该注意的是,右值引用类型的变量,它本身是个左值

比如说:int &&x = 10,x的类型是右值引用,但它本身是个左值

如果你要把x再次当成右值传为另一个函数或者赋值给另一个数,那么直接传是不行的,这个时候就要用到std::move()

四. 强转右值std::move()

std::move()可以将左值强制转化为右值,类型上变成右值引用类型

作用是把原对象指向堆内存空间的指针地址转交给新对象,然后将原对象的该指针

置为nullptr

但是std::move()对基本类型,如int,double,char,指针无效,因为他们的值就存在于栈上,不指向任何外部资源

比如说:

int res = 1; int ans = std::move(res); std::cout << res << "\n";

此时你会发现输出的仍是1

如果不是的话,那么原对象会被“掏空”,比如说:

std::string s = "abc"; std::string t = std::move(s); std::cout << s << "\n";

输出的就是空

  • 注意,只有在std::move()用于创建新对象时,才触发移动构造

五. 纯右值与将亡值

1. 纯右值

没有身份,没有固定内存地址,不可被移动,用完即销毁的值/表达式

特点:

  • 没有标识符(没有名字)
  • 不能取地址
  • 一般用于初始化和计算

2.将亡值

有身份,可被移动的值/表达式

特点:

  • 通常对应一个右值引用

  • 在内存中有地址

  • 通过std::move()强转后的就是将亡值

六. 函数参数传递

问:这六次函数调用,分别各触发了几次拷贝,几次移动构造?

void f1(Node v){ return; } void f2(Node &v){ return; } void f3(Node &&v){ return; } int main(){ Node a; Node &b = a; Node c; Node d; f1(a);//1次拷贝,0次移动构造 f1(b);//1次拷贝,0次移动构造 f1(std::move(c));//0次拷贝,1次移动构造 f2(a);//0次拷贝,0次移动构造 f2(b);//0次拷贝,0次移动构造 f3(std::move(c));//0次拷贝,0次移动构造 return 0; }

七. 引用折叠

规则:只要有左值引用,结果就是左值引用;只有当两个都是右值引用时,结果才是右值引用

原始类型(T)声明的引用类型折叠后结果
T&&T&
T&&&T&
T&&&T&
T&&&&T&&

八. 万能引用

万能引用的好处在于既能绑定左值,又能绑定右值

万能引用形式上为T&&,但要成为万能引用,必须满足两个条件:

  • 必须存在类型推导,如模板,auto
  • 形式只能是T&&,如果用const修饰,就会变回普通的右值引用

易错区分:

void func(std::vector<T> &&v){ return; }

这不是万能引用!

因为它被固定为了std::vector右值引用。它只能接收std::vector的右值,不能接收std::vector的左值,更不能接收其他的类型(如int

万能引用的形式只能是T&&

  • 万能引用是贪婪的,这里模板推导的“精确匹配”优先级高于普通函数的“隐式转换”,也就是说可能会引发不必要的引用转化

九.完美转发std::forward<T>

用处是当一个函数把参数传递为另一个函数时,保留参数的原生属性

例:

void target(int&& x) { return; } template<typename T> void wrapper(T&& param) { target(param); //报错 } int main() { wrapper(10); }

在这个例子中,10确实是右值,但是在wrapper函数中,param是左值,无法进行&&的右值绑定,于是会报错

为了解决这个问题,我们引入了std::forward<T>,它能根据T推导的结果,决定把param转化为左值或者是右值,于是就可以:

template<typename T> void wrapper(T&& param) { target(std::forward<T>(param)); }
http://www.jsqmd.com/news/1107634/

相关文章:

  • 图解人工智能(73)人工智能前沿-预测化学反应分类
  • 【紧急修复必备】IDEA Git历史回滚黄金法则:3类不可逆操作预警+4种安全回滚路径(含可视化操作图谱)
  • OpenMP并行编程优化与性能调优实践
  • 运营商领域全生命周期数据安全合规管控技术落地实践与格局梳理
  • AI生成多页面原型实战:APP与B端后台的生成效果与流程拆解
  • 电子电路与PCBA:从概念到可制造组装
  • 大模型Agent开发:从原理到实战的必备指南
  • 微信支付V3平台证书切换公钥验签:从“无可用证书”到Base64解码错误的实战解决方案
  • 私域直播SaaS横向测评:保利威、诺云、悦邻,谁更懂“社区门店”的生意逻辑?
  • 仿小红书源码架构解析,瀑布流、Redis缓存、搜索与WebSocket消息设计
  • Linux deb 安装包转 rpm 安装包
  • 如何高效使用抖音无水印下载工具:专业用户的完整方案指南
  • 跨区公有云节点 DNS 解析故障排查与自动化修复记录
  • 【Git Diff可视化权威标准】:基于JetBrains官方API文档逆向验证的12项IDEA差异比对最佳实践
  • 国家中小学智慧教育平台电子课本下载器:三步获取PDF教材的完整方案
  • STM32F411RE键盘扩展方案:74HC32实现16功能输入
  • 【Spring Boot项目结构黄金标准】:20年架构师亲授5大不可违背的模块划分铁律
  • 2026年亲测AI论文写作软件合集(合规高效版)
  • 紧急修复!IDEA 2024.2书签同步失效Bug应对方案(含临时补丁+长期配置优化双路径)
  • 2026年AI客服选型实战指南:咨询量高但留资率上不去?四步搭建7×24小时自动留资系统
  • 图解人工智能(75)人工智能前沿-预测化学反应分类
  • nRF9151 面向蜂窝物联网
  • 2026年正规1688代运营服务商 TOP10榜
  • 光伏硅片金刚线切片工位TS-h2490FU全闪存阵列架构
  • 收藏!AI时代普通人逆袭指南:比互联网大10倍的机会,你准备好了吗?
  • 3个步骤快速实现ESP-SR嵌入式语音识别:智能设备离线语音控制完整指南
  • AI评审发现的高频问题汇总
  • RAG不挂知识库就是裸奔——企业知识库与大模型怎么接
  • AI 时代采购人不被替代:CPPM 帮你打造核心竞争力
  • Privazer源码开发避坑实战指南