C++11 面试题插入(左值引用和右值引用的区别)移动构造和移动赋值C++新标准如何解决传值返回但对象销毁问题

C++11 面试题插入(左值引用和右值引用的区别)移动构造和移动赋值C++新标准如何解决传值返回但对象销毁问题

🎬 胖咕噜的稞达鸭个人主页

🔥 个人专栏: 《数据结构《C++初阶高阶》《算法入门》

⛺️技术的杠杆,撬动整个世界!


在这里插入图片描述


在这里插入图片描述

列表初始化

  1. 内置类型初始化
int x{2};int x1=2;
  1. 自定义类型初始化
  • 2.1 直接构造
    本质是先构造一个Date临时对象,
    再拷贝构造d1;但编译器会优化这个过程,直接用列表参数构造d1(即不会调用拷贝构造函数)。
//2.自定义类型(类)的列表初始化// 2.1可以直接构造,本质是先构造一个Date临时对象,// 再拷贝构造d1;但编译器会优化这个过程,直接用列表参数构造d1(即不会调用拷贝构造函数)。 Date d1 ={2025,11,01}; Date d2{2025,05,28};
  • 2.2 绑定引用初始化
    列表{ 2025,12,12 }会先构造一个Date临时对象,然后将这个临时对象的引用绑定到const引用d3(或d4)上。
    因为临时对象的生命周期会被延长,与const引用的生命周期一致,所以这种写法是合法的。
const Date& d3 ={2025,12,12};const Date& d4{2025,12,12};//Date& d4{ 2025,12,12 };// 临时对象 { 2025,12,12} 被绑定到const引用d4,生命周期延长至d4的作用域结束

问题:const Date& d4(2025,10,10)为什么一定要加const,const意义何在?
如果不加const ,就是费const引用,会报错,原因在于非const引用意味着可以修改内部的数据,但是受生命周期影响,被引用&的内容出了作用域就会销毁,再去修改内部的数据,就会报错。加const一来可以防止内部的数据被修改,而来可以让d4一直坚持到生命周期结束再销毁。
“const引用可以延长临时对象的生命周期,使其与引用自身的生命周期一致”,这样就避免了** “对象提前销毁导致引用失效” **的问题。
简单总结一下:
临时对象匿名对象的生命周期都只在一行,const 引用可以延长临时对象+匿名对象的生命周期。

非const引用 + 临时对象 → 语法禁止(因为修改无意义且危险);
const引用 + 临时对象 →语法允许,且延长临时对象生命周期至引用销毁。

注意一个小点:只有{ }初始化才可以省略=
像Date d 2025;一定会报错

结合析构函数(复习巩固)

在这里插入图片描述

局部对象和被const引用延长生命周期的临时对象,析构顺序与构造顺序相反(即 “先构造的后析构,后构造的先析构”)。
在这段代码中,构造顺序大致是:d1 → d2 → d3 → d4;因此析构顺序是:d4 → d3 → d2 → d1,与打印的顺序完全一致。

//构造 vector<int>v1 ={1,2,3,4,5,6}; vector<int>v2{1,2,3,4,5,6};const vector<int>& v3 ={9,8,7,6,5,4};const vector<int>&v4({9,8,7,6,5,4});//构造+拷贝构造+优化 vector<int>v5({6,7,8});//({6,7,8})通过列表构造函数创建临时对象,然后用该临时对象直接构造v5。//编译器会触发拷贝构造优化(返回值优化,RVO),//避免临时对象的拷贝,直接在v5的内存地址上构造对象,最终等价于一次构造。

列表初始化+pair隐式类型转换

map<string,int>map1 ={{"apple",5},{"bule",9}};//map<string, int> & map2({ "apple",5 }, { "bule" ,9 });报错没有匹配的构造函数的类型

右值引用和移动语义

左值和右值的区别:右值不可以取地址,左值可以取地址。

左值引用给左值取别名,右值引用给右值取别名。

//左值引用给左值取别名int& r1 = b;int*& r2 = p;int& r3 =*p; string& r4 = s;char& r5 = s[0];//右值引用给右值取别名int&& rr1 =10;double&& rr2 = x + y;double&& rr3 =fmin(x, y); string&& rr4 =string("11111");

左值不能直接引用右值,但是加了const的左值可以引用;
右值也不能直接引用左值,但是可以引用move左值。
补充:move的本质就是进行强制类型转换。

在这里插入图片描述


在这里插入图片描述


注意:
从语义和内存本质的角度,左值引用和右值引用都是 “别名”,本身不额外开辟新的对象存储空间,在底层实现上通常以指针的形式存在(汇编层面会体现为指针操作)
**右值引用可以给右值起别名,但右值引用变量本身是左值属性。**所以变量表达式都是左值属性。

引用延长生命周期

总结:
右值引用可以延长被引用对象的生命周期,被引用对象可以通过非const的引用修改;
左值引用不能延长对象生命周期,但是const左值引用可以延长生命周期,
被引用对象不能通过到const的引用修改。

在这里插入图片描述
intmain(){ std::string s1 ="happy"; std::string&& a1 = std::move(s1);const std::string& a2 = s1 + s1;//到const的左值引用延长生命周期//a2 += "happy";//error:没有与这些操作数匹配的 "+=" 运算符//错误:不能通过到 const 的引用修改 std::cout << a2 <<'\n';//print:happyhappy std::string&& a3 = s1 + s1;// 右值引用延长生存期 a3 +="exersice";// 能通过到非 const 的引用修改 std::cout << a3 <<'\n';//print:happyhappyexersicereturn0;}

左值和右值的参数匹配

C++11以后,分别重载左值引用、const左值引用、右值引用作为形参的f函数,那么实参是左值会匹配f(左值引用),实参是const左值会匹配f(const左值引用),实参是右值会匹配f(右值引用)。

#include<iostream>#include<string>usingnamespace std;voidf(int& x){ cout <<"左值引用重载调用"<< endl;}voidf(constint& x){ cout <<"const左值引用重载调用"<< endl;}voidf(int&& x){ cout <<"左值引用重载调用"<< endl;}intmain(){int i =1;constint ci =2;int&& x =3;f(1);//print:左值引用重载调用f(ci);//print:const左值引用重载调用f(x);//print:左值引用重载调用f(std::move(x));// print:右值引用重载调用(原则上,但是编译器版本太新了会做优化调用左值引用return0;}

右值引用和移动语义的使用场景

面试题插入:左值引用和右值引用的区别:

左值引用是 “对象的别名”,用于共享访问和避免拷贝;
右值引用是 “临时资源的所有权标识”,用于移动语义和完美转发,是 C++11 后提升性能的关键特性。
左值引用和右值引用的目的都是减少拷贝,提高效率。

优点:
左值引用可以修改参数/返回值,方便使用;
缺点:
但是在有些场景下,左值引用在对象函数栈帧中结束了会销毁,不能使用左值引用返回,当前函数局部对象,出了当前函数作用域生命周期到了就销毁了,不能用左值返回,只能传值返回。
问题:
那么如何解决只能传值返回,但是返回对象在函数栈帧结束之后会销毁的问题?

解决场景:
方案一:
不用返回值,用输出型参数。不足:一定程度上牺牲了可读性;
方案二:
编译器优化
方案三:
新标准新语法处理(右值引用),而右值引用可以延长函数对象的生命周期

编译器不优化的场景是拷贝构造+拷贝赋值,VS2019debug版本传参有优化,,对于ret赋值的没有优化到还是有拷贝构造和拷贝赋值,二代优化只有一次拷贝赋值,没有出现临时对象,没有拷贝构造。将构造和拷贝构造合二为一。
C++11之后彻底不优化,但是调用了移动赋值和移动构造,这样会直接调用移动构造和移动赋值,传值返回的代价约等于0.

总结:

在没有移动构造和移动赋值的情况下:

构造场景:

编译器如果不优化:
先产生一个临时对象,str函数哈哈栈帧结束后,临时对象接收str中存储的数据(一次拷贝构造),临时对象又会将内部存储的值拷贝构造给main()函数栈帧中的ret.总体上来说是两次拷贝构造。

在这里插入图片描述
string addstring1(string nums1,string nums2){ string str;int end1=nums1.size()-1,end2=nums2.size()-1;int next=0;... str+=('0'+ret);...return str;}intmain(){//构造场景 string ret=addstring1("xndx","lzdx"); cout<<ret.c_str()<<endl;return0;}

不优化:会调用很多次构造先构造参数,每一次参数的构造完成,还会调用拷贝构造;
一代优化:合二为一,没有临时对象的产生,只产生了一次拷贝构造,构造+拷贝构造。
二代优化:合三为一,直接构造。

赋值场景:

在这里插入图片描述
string addstring1(string nums1,string nums2){ string str;int end1=nums1.size()-1,end2=nums2.size()-1;int next=0;... str+=('0'+ret);...return str;}intmain(){//赋值场景 string ret; ret=addstring1("xndx","lzdx"); cout<<ret.c_str()<<endl;return0;}

不优化版本下:
产生一个临时对象,str在函数栈帧结束之前将内部资源拷贝构造给临时对象,由临时对象拷贝赋值给main函数中的ret。参数构造+拷贝构造,最后还有一次拷贝赋值

一代优化:多个参数构造,一次拷贝构造+一次拷贝赋值;
二代优化:拷贝构造和构造合二为一,最后一次拷贝赋值。

有移动构造和移动赋值C++11环境下:

构造场景:

在这里插入图片描述

不优化版本:str先将内部资源移动构造给临时对象,main函数中ret接收临时对象中的资源,也是移动构造,每一个参数构造+移动构造;

1代优化:没有产生临时对象,编译器识别到 str 是 “即将被返回的局部对象”,会将其视为右值;执行 return str; 时,触发移动构造:直接将 str 的资源 “转移” 给临时对象(而非拷贝);
进一步优化中,临时对象和 main 中的 ret 会 “合二为一”(省略临时对象的构造),最终 str 的资源直接移动构造到 ret 中。
此过程仅触发一次移动构造,资源转移效率极高(无额外拷贝)。构造参数+最终一次移动构造

2代优化(比如在VS2022编译器下)中甚至没有产生str,只有ret,也就是说str本质是ret对象的引用,实际上没有产生str,一旦产生,函数栈帧销毁,局部对象str销毁,ret就是野引用,所以str没有产生,其底层使用指针形式实现,这个时候打印出str和ret的地址cout<<&ret<<endl;cout<<&str<<endl;会发现他们的地址是相同的。
这时候会直接构造,没有移动构造。

赋值的场景:

在这里插入图片描述
在这里插入图片描述
string addstring1(string nums1,string nums2){ string str;int end1=nums1.size()-1,end2=nums2.size()-1;int next=0;... str+=('0'+ret);...return str;}intmain(){//赋值场景 string ret; ret=addstring1("xndx","lzdx"); cout<<ret.c_str()<<endl;return0;}

不优化版本下:
产生一个临时对象,str在函数栈帧结束之前将内部资源拷贝构造给临时对象,由临时对象拷贝赋值给main函数中的ret。参数构造+移动构造,最后还有一次移动赋值
一代优化:多次构造,一次移动构造+一次移动赋值;
二代优化:构造和移动构造合二为一,只有构造和移动赋值。

总体来说:
如果代码中有拷贝构造和拷贝赋值,也有移动构造和移动赋值,编译器一定会优先执行移动构造和移动赋值,因为选择的都是效率高的。
当编译器升级+C++支持移动构造和移动赋值,传值返回的效率变高。

问题:那么移动构造和移动赋值的效率这么高,需不需要在每一个类型中都实现?
对于深拷贝的自定义类型(vector/string/map…),实现移动构造和移动赋值的价值很大,一定程度上比拷贝构造和拷贝赋值的效率高很多。
对于浅拷贝的自定义类型(如Date/pair<int,int>…)不需要额外实现移动构造和 移动赋值,因为浅拷贝的传值返回和移动构造移动赋值相比,浅拷贝没有指向资源,拷贝代价不大,效率差不多。

类别:

在这里插入图片描述


在这里插入图片描述


引用折叠

类型别名的引用折叠:

intmain(){typedefint& lref;//lref 是 “int 的左值引用” 类型typedefint&& rref;//rref 是 “int 的右值引用” 类型。int n =0; lref& r1 = n;// r1 的类型是 int& lref&& r2 = n;// r2 的类型是 int& rref& r3 = n;// r3 的类型是 int& rref&& r4 =1;// r4 的类型是 int&&return0;}

总结:
只要嵌套中存在左值引用(&),最终结果就是左值引用;只有纯右值引用(&&)嵌套时,才是右值引用。
二、模板函数的引用行为

// 由于引用折叠限定,f1实例化以后总是一个左值引用template<classT>voidf1(T& x)//模板参数是左值引用,实例化的时候只能接受左值(n),无论T是什么类型,x始终是左值引用(无折叠空间,因为参数已固定为& )。{}// 由于引用折叠限定,f2实例化后可以是左值引用,也可以是右值引用template<classT>voidf2(T&& x)//模板参数是万能引用(T&&){}

f1(T& x):模板参数是左值引用,实例化时只能接受左值(如 n),且无论 T 推导为何,x 始终是左值引用(无折叠空间,因为参数已固定为 &)。
f2(T&& x):模板参数是万能引用(T&&)
实例化时:
传入左值(如 n),T 推导为 int&,x 类型为 int& && → 折叠为 int&(左值引用)。
传入右值(如 1),T 推导为 int,x 类型为 int&&(右值引用)。

结论:f1 只能处理左值引用,f2 可通过引用折叠同时处理左值和右值引用(万能引用的特性)。

在这里插入图片描述


f1(T& x) 本质是左值引用参数,显式实例化后仍为左值引用,非const时仅接受左值,const时可接受左值和右值。

在这里插入图片描述


f2(T&& x) 是万能引用,显式实例化后通过引用折叠可变为左值或右值引用,需严格匹配实参的左值 / 右值属性。

总结:左值只能绑定左值引用,右值引用可以绑定右值引用和const左值引用。

// 由于引用折叠限定,f1实例化以后总是一个左值引用template<classT>voidf1(T& x)//模板参数是左值引用,实例化的时候只能接受左值(n),无论T是什么类型,x始终是左值引用(无折叠空间,因为参数已固定为& )。{}// 由于引用折叠限定,f2实例化后可以是左值引用,也可以是右值引用template<classT>voidf2(T&& x)//模板参数是万能引用(T&&){}intmain(){typedefint& lref;//lref 是 “int 的左值引用” 类型typedefint&& rref;//rref 是 “int 的右值引用” 类型。 lref& r1 = n;// r1 的类型是 int& lref&& r2 = n;// r2 的类型是 int& rref& r3 = n;// r3 的类型是 int& rref&& r4 =1;// r4 的类型是 int&&int n =0;//没有折叠->实例化为void f1(int& x) f1<int>(n);//n是左值,可绑定到int&; f1<int>(0);//0是右值,无法绑定到非const的int& → 报错。// 折叠 f1<int&>(n);//n是左值,可以绑定到int& & f1<int&>(0);// 0是右值,无法绑定,改正:f1<const int&>(0);// 折叠 f1<int&&>(n);//T=int&& → 参数类型为(int&&)&(引用折叠为int&)。n(左值)可绑定到int& ; f1<int&&>(0);// //0(右值)不可 → 后者报错。// 折叠->实例化为void f1(const int& x) f1<constint&>(n);//T=const int& → 参数类型为const int&(const左值引用)。 f1<constint&>(0);//左值(n)和右值(0)都能绑定到const int& → 均合法。// 折叠 f1<constint&&>(n);//const右值引用 f1<constint&&>(0);// 没有折叠->实例化为void f2(int&& x) f2<int>(n);// T = int → 参数类型为int && (右值引用)。 f2<int>(0);// 0是右值,参数类型是int &&(右值引用)// 折叠->实例化为void f2(int& x) f2<int&>(n);//T = int& &&,n是左值,左值引用和左值,不报错 f2<int&>(0);//左值引用和0会报错,左值无法引用右值,报错改正:f2<const int&>(0);// 折叠->实例化为void f2(int&& x) f2<int&&>(n);// T=int&& → 参数类型为(int&&)&&(折叠为int&&,右值引用)。n(左值)无法绑定到int&& ; f2<int&&>(0);//0(右值)可绑定 → 前者报错。return0;}

Function(T&& t)函数模板程序中,假设实参是int右值,模板参数T的推导int,实参是int左值,模板参数T的推导int&,再结合引用折叠规则,就实现了
实参是左值,实例化出左值引用版本形参的Function,
实参是右值,实例化出右值引用版本形参的Function。

在这里插入图片描述
template<classT>voidFunction(T && t)//模板参数是万能引用(T&&){int a =0; T x = a;//x++;// 所以Function内部会编译报错,x不能++,error:不能给常量赋值 cout <<&a << endl; cout <<&x << endl << endl;}intmain(){Function(10);// 10是右值,推导出T为int,模板实例化为void Function(int&& t)int a;Function(a);// a是左值,推导出T为int&,引用折叠,模板实例化为void Function(int& t)Function(std::move(a));// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)constint b =8;Function(b);//b是左值,推导出T为const int&,引用折叠,模板实例化为void Function(const int&& t)Function(std::move(b));// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&&t)return0;}
在这里插入图片描述

Read more

Python提取Word文档中各种数据的详细方法

使用Python提取Word文档中各种数据的详细方法 介绍如何利用Python高效提取Word文档(.docx格式)中的数据。Word文档常用于存储文本、表格、图片、列表等结构化信息,通过自动化提取,可以提升数据分析和处理的效率。本文基于Python库python-docx(官方文档:python-docx文档),逐步讲解安装、基础操作和高级技巧。所有代码示例均经过测试,确保真实可靠。 1. 准备工作:安装和导入库 首先,安装python-docx库。使用pip命令: pip install python-docx 导入库并加载Word文档: from docx import Document # 加载Word文档,假设文件名为"example.docx" doc = Document("example.docx") 如果文档路径不确定,可以使用相对路径或绝对路径。确保文件存在,否则会抛出异常。 2. 提取文本内容 文本是Word文档的核心,

By Ne0inhk
Gemini 3.0 Pro 与 Sora 2 震撼发布:Python 开发者如何用“上帝视角”构建下一代多模态应用?(附全链路源码)

Gemini 3.0 Pro 与 Sora 2 震撼发布:Python 开发者如何用“上帝视角”构建下一代多模态应用?(附全链路源码)

摘要: 2026 年伊始,AI 圈再次迎来核爆级更新。 Google 的 Gemini 3.0 Pro 彻底打破了上下文窗口的物理极限。 神秘的 Banana2 模型以其惊人的“思维链”推理能力霸榜 HuggingFace。 而视频生成领域的“双子星” Sora 2 与 Google Veo 3 更是将 AI 视频带入了 4K/60FPS 的电影级时代。 然而,面对高达 20+ 种的新增模型和碎片化的 API 文档,开发者该如何接招? 本文将从底层技术原理出发,深度解析新一代模型的架构革新。 并手把手教你利用 向量引擎(Vector Engine) 的聚合能力。 在 10 分钟内搭建一套支持“文本-思考-视频”

By Ne0inhk

Python自动化PPT神器:python-pptx 库从入门到实战(附5个实用案例)

在日常工作中,你是否经常需要制作重复性的PPT?比如每月的业务报告、批量生成产品介绍、学生成绩单展示等。手动调整格式、输入数据不仅耗时,还容易出错。而 python-pptx 库的出现,让PPT制作进入了"代码驱动"时代——用几行代码就能自动生成结构规整、格式统一的PPT,大幅提升效率。 本文将从基础用法到实战场景,全方位讲解 python-pptx 的使用方法,让你彻底告别手动制作PPT的繁琐。 一、什么是 python-pptx?为什么需要它? python-pptx 是一个用于创建和修改 Microsoft PowerPoint(.pptx)文件的Python库,它的核心优势在于: * 自动化生成:通过代码批量创建幻灯片,避免重复劳动,尤其适合定期报告、批量文档等场景。 * 格式精准控制:精确设置字体、颜色、布局、位置等,确保PPT风格统一,比手动调整更规范。 * 数据联动:直接从Excel、数据库等数据源读取内容,

By Ne0inhk
【算法工程师】—— Python 数据分析

【算法工程师】—— Python 数据分析

Python 数据分析 Numpy 特点: * ndarray:N维数组对象,快速高效 * 向量化操作:避免循环,提高性能 * 广播机制:不同形状数组的运算 * 丰富的数学函数:线性代数、傅里叶变换等 数组创建与初始化 函数/方法作用参数示例np.array()从列表/元组创建数组np.array([1,2,3])np.zeros()创建全0数组np.zeros((3,3))np.ones()创建全1数组np.ones((2,4))np.full()创建指定值数组np.full((3,3), 5)np.arange()创建等差数组np.arange(0, 10,

By Ne0inhk