尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

C++移动语义

C++移动语义
📅 发布时间:2026/7/2 8:05:30

一. 浅拷贝与深拷贝

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)); }

相关新闻

  • 科研工作者如何用3个步骤建立高效的知识管理系统
  • 运营商领域全生命周期数据安全合规管控技术落地实践与格局梳理
  • 电子电路与PCBA:从概念到可制造组装

最新新闻

  • VMware USB直通安全边界被突破?首次披露CVE-2023-21989漏洞利用链:如何在启用直通时强制隔离USB控制器DMA通道
  • 【TEE从入门到精通及实战】89 TEE密钥全生命周期管理:从生成到销毁的实战指南
  • 如何快速掌握鸣潮游戏优化:5步终极指南释放你的硬件潜力
  • 工业级运动跟踪:ASM330LHH与PIC18LF45K80实战解析
  • 江西大诺营造丨度假酒店、商业空间全案落地与空间陈设实用攻略
  • 终极Mac窗口置顶神器Topit:三步实现多窗口悬浮显示

日新闻

  • Python Playwright录制功能:从零到一构建自动化测试脚本
  • 如何用开源工具永久保存你心爱的小说:novel-downloader全攻略
  • In-Context Learning不是教知识,而是模式对齐:从5个示例到100个工业级样本的真相

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号