首页
学习
活动
专区
圈层
工具
发布

【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略

在本篇中主要使用VS2019和VS2022来进行比较,因为实际情况的复杂性,以及编译器版本的不同,甚至同一大版本中小版本的不同更新的VS都存在一定的差异,本篇输出结果示例仅作参考,更多的是让读者通过不同优化的比较来理解现代编译器在提升程序效率所做的改进...输出结果: A(int a) 构造函数被调用, _a = 10 ~A() 析构函数被调用 解释: 在 VS2022 中,拷贝构造函数被优化掉,编译器直接将原对象 aa1 传递给函数 f1。...在 C++ 中,赋值操作是将一个对象的内容复制到另一个对象中。这与对象的构造不同,因为在赋值操作时,目标对象已经存在,不能通过构造优化来避免对象的状态复制。...~A() 析构函数被调用 ~A() 析构函数被调用 ~A() 析构函数被调用 在 VS2019 中,即使返回的是局部变量,仍会创建一个临时对象,然后通过拷贝构造将其传递给 a3。...VS2022 的输出结果: A(int a) 构造函数被调用, _a = 1 ~A() 析构函数被调用 在 VS2022 中,编译器能够更好地分析对象生命周期,跳过了临时对象的创建,直接在 a3 的内存空间中构造返回的局部变量

71810

【C++篇】类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略

输出结果: A(int a) 构造函数被调用, _a = 10 ~A() 析构函数被调用 解释: 。在 VS2022 中,拷贝构造函数被优化掉,编译器直接将原对象 aa1 传递给函数 f1。...创建一个临时对象,将 aa 拷贝到这个临时对象中。 最后将临时对象拷贝给 a2,并调用两次拷贝构造函数。...在 C++ 中,赋值操作是将一个对象的内容复制到另一个对象中(与赋值运算符重载相似)。这与对象的构造不同,因为在赋值操作时,目标对象已经存在,不能通过构造优化来避免对象的状态复制。...A& aa) 拷贝构造函数被调用 ~A() 析构函数被调用 ~A() 析构函数被调用 ~A() 析构函数被调用 在 VS2019 中,即使返回的是局部变量,仍会创建一个临时对象,然后通过拷贝构造将其传递给...VS2022输出结果: A(int a) 构造函数被调用, _a = 1 ~A() 析构函数被调用 在 VS2022 中,编译器能够更好地分析对象生命周期,跳过了临时对象的创建,直接在 a3 的内存空间中构造返回的局部变量

55710
  • 您找到你想要的搜索结果了吗?
    是的
    没有找到

    深入理解 C 语言中的 static 关键字:三大核心应用场景解析

    1.1 实例对比:普通局部变量 vs static 局部变量 我们通过一段代码直观感受差异: #include // 普通局部变量 void test_normal() { int...1.3 典型应用场景 需要在函数多次调用间 “保留中间结果” 时,比如计数器(统计函数被调用的次数)、累加器(累计多次计算的结果)等。...3.1 实例对比:普通函数 vs static 函数 同样用main.c和module.c两个文件演示: 场景 1:普通函数(可跨文件调用) module.c(定义普通函数): int add(int...int b); // 声明:在其他文件中定义了add函数 int main() { int result = add(3, 5); printf("普通函数计算结果:%d\n", result); /...3.3 典型应用场景 当函数仅作为 “当前文件的工具函数”(比如辅助当前文件的其他函数完成计算)时,用static修饰可隐藏函数实现细节,避免外部文件误调用,同时防止同名函数冲突(尤其在大型工程中)。

    82410

    【笔记】《C++Primer》—— 第6章:函数

    ) 建议函数的声明与定义要分开来写,因为函数可以声明多次但只能定义一次,声明建议写在头文件中 形参名是可选的,但是不写形参的名字会使得我们无法在函数中使用这个形参且降低可读性,所以建议都写上形参,即使在声明中也一样...返回值的原理实际上用结尾的return的值初始化一个临时量作为结果,也是拷贝得到的,所以要谨记不要返回不可拷贝的局部变量,也不要返回对局部变量的引用或指针,例如不要返回局部数组的指针。...,不允许间断 默认实参声明后不能再修改,但是函数可以多次添加默认实参 尽管局部变量不能成为函数的默认实参,但是常量表达式可以,函数名字的计算会在函数调用时才进行 通过inline关键字可指定某函数为内联函数...但实际上constexpr会隐式展开为内联函数,而且形参也可以不是常量,只是此时返回值也将不会是常量了 要注意内联函数和constexpr由于需要在调用处随时展开,所以需要多次定义,最方便的方法就是将他们的定义写在头文件中...,而inline函数等常常定义在头文件中,又头文件常常要被许多函数引用,那么我们就需要使用预处理器语句来防止头文件的重复引用造成数据的重复定义了 具体来说使用头文件保护符来解决,有些编译器(如vs)支持

    1K30

    如何分析和提高(CC++)程序的编译速度?

    一个别人的vs 2010 的程序, 编译, 加载数据, 运行, 需要个把小时。当改代码然后再运行的时候,又要个把小时才能编译看结果.这样岂不是很浪费时间, 怎么办?这样如何修改程序,怎么提高效率啊?...硬件、编译器造成的 使用好点的电脑无疑是一个操作上的最佳选择,其次,对于编译器也是可以编译选项优化的,例如在VS环境中,可以通过配置属性来实现,具体步骤如下,大家可以参考:https://blog.csdn.net...我们来看看Primer中怎么说的 当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。...、静态局部变量、全局变量与静态全局变量 局部变量是存在于堆栈中的,对其空间的分配仅仅是修改一次esp寄存器的内容即可; 静态局部变量是定义在函数内部的,静态局部变量定义时前面要加static关键字来标识...,静态局部变量所在的函数在多调用多次时,只有第一次才经历变量定义和初始化; 当一个文件或者数据反复使用时,应该存储在全局变量中,避免重复加载使用; 静态全局变量是静态存储方式,静态全局变量则限制了其作用域

    1.7K51

    JavaScript-作用域、块级作用域、上下文、执行上下文、作用域链

    局部作用域只在该函数调用执行期间存在。 七、上下文 vs 作用域 (1)首先需要说明的是上下文和作用域是不同的概念。 (2)每个函数调用都有与之相关的作用域和上下文。...(2)函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文。 (3)当javascript代码文件被浏览器载入后,默认最先进入的是一个全局的执行上下文。...10.2 攀爬作用域链 (1)当不同执行上下文之间存在 变量命名冲突,可以通过攀爬作用域链解决(从顶部到底部)。...这也就是说 在最内层函数(执行栈顶部的执行上下文)中,具有相同变量名称的变量将具有较高优先级。 (2)简单的说,每次试图访问函数执行上下文中的变量时,查找进程总是从自己的变量对象开始。...如果在自己的变量对象中没发现要查找的变量,继续搜索作用域链。他将攀爬作用域链检查每一个执行上下文的变量对象,去寻找和变量名称匹配的值。

    4.6K22

    JVM内存模型——运行时数据区的特点和作用

    ,并且将计算结果压栈中,接着就是istore这个指令,istore就是将暂定的操作数取出来,并存放在局部变量表。...,b方法入栈之后,会将 a和b两个操作数入栈,通过求和字节码指令计算结果,并且将计算结果存入局部变量表。...那么这个中间的结果又将会成为栈帧a的操作数,所以需要从栈帧b中的局部变量表中将该值复制记录到栈帧a的操作数栈。...这样做是没有问题的,但是jvm觉得这里可以优化,在jvm的实践中,将两个栈帧的一部分重叠,让下面的栈帧的操作数栈和上面栈帧的部分局部变量表进行重叠。...而对于有些方法,因为多态的存在,JVM无法在加载的阶段就确定被调用的具体类型,只能在运行时真正产生调用的时候。根据实际类型信息来进行连接,这就叫做动态连接.

    88220

    【Python】链式、嵌套调用、递归、函数栈帧、参数默认值和关键字参数

    的值,然后将 add 的结果传给 isOdd 进行奇偶数判断,最后将结果给 print 进行打印 链式调用中,是先执行 ( ) 里面的函数,然后执行外面的函数。...每个函数的局部变量就保存在对应的栈帧之中 调用函数,则生成对应的栈帧;函数结束,则对应的栈帧消亡(里面的局部变量也就没了) 如果将每个函数里面的变量名都改为一样的,但里面的变量仍是不同的变量,属于不同的函数作用域...每个变量是保存在各自的栈帧中的,每个栈帧是保存在内存上的 变量的本质是一块内存空间 函数递归 函数递归,就是一个函数自己调用自己 def factor(n): if n == 1:...n,但是 n 是函数的形参,形参相当于函数的局部变量,而局部变量是在函数自己的栈帧上的 虽然几个函数的局部变量名相同,但是他们是在不同的栈帧中,是在不同的内存空间中,也就是不同的变量 另一方面,看起来是同一个函数...,但是这里的多次调用,其实是多个不同的栈帧 递归的两个要素: 递归结束条件 递归的递推公式 缺点 递归的缺点: 执行过程非常复杂,难以理解 递归代码容易出现“栈溢出”的情况 代码不小心写错了,导致每次递归的时候

    48810

    Java基础(七): 面向过程与面向对象、类与对象、成员变量与局部变量、值传递与引用传递、方法重载与方法重写

    } } 三、成员变量 vs 局部变量 1、成员变量 定义:直接声明在类内部、方法/代码块外部的变量 分类: 实例变量(非 static) 类变量(static,静态变量) 示例: public class...,随对象回收而销毁类变量:随类加载而存在,随程序结束而销毁 存储位置 堆内存(实例变量在对象内)、方法区(类变量) 初始化 自动赋予默认值(如 int 默认为 0,引用类型默认为 null) 访问方式...、代码块或形参列表中的变量 分类: 方法局部变量 代码块局部变量 参数变量(方法形参) 示例: public void printInfo(int age) { // age是参数(局部变量)...对于引用类型,传递的是引用的值(即对象地址的副本),而非引用本身(即不是引用传递) 五、方法重载 vs 方法重写 1、方法重载(Overloading) 定义:在同一个类中,多个方法使用相同的方法名但参数列表不同...(参数类型、个数或顺序不同) 核心特点: 相同类中:所有重载方法必须在同一个类(或父子类中也可重载) 方法名相同:必须使用完全相同的名称 参数列表不同: 参数类型不同(如 int vs String

    26400

    C语言进阶:(一)深度剖析函数栈帧:从创建到销毁

    前言 在 C 语言编程中,我们每天都在与函数打交道 —— 将复杂功能拆解为独立函数、通过函数调用实现逻辑复用、依赖函数返回值传递计算结果。...本文将基于 VS2019 编译器,从栈的基础概念出发,结合汇编指令与实战代码,一步步拆解函数栈帧的创建、函数调用、参数传递、返回值带回及栈帧销毁的完整过程,带你看透 C 语言函数执行的底层逻辑。...) pop 出栈指令 将栈顶数据弹出到指定寄存器 / 内存,esp 自动加 4 pop edi(将栈顶值弹出到 edi 寄存器) sub 减法指令 两个操作数相减,结果存放在第一个操作数中 sub esp..., 0E4h(esp = esp - 0E4h) add 加法指令 两个操作数相加,结果存放在第一个操作数中 add esp, 8(esp = esp + 8) call 函数调用指令 1....因为如果程序多次调用函数,栈帧会重复使用这块内存,上一次函数执行后残留的数据可能会覆盖 0xCCCCCCCC,导致未初始化的局部变量值不确定。

    16410

    Java基础——面向对象

    ,那么就用刚被赋的值替换掉原来的初始值 D:如果在main方法中调用对象的方法,就直接从方法区里面把方法加载到栈内存中,进行执行,执行完了从栈内存消失 ?...成员变量和局部变量 类中位置不同 成员变量:在类中方法外 局部变量:方法内 内存中位置不同 成员变量:在堆内存中 局部变量:栈内存中 生命周期不同 成员变量:随着对象的创建(new的时候创建...)而存在 随着对象的消失(当这个内存区域没有引用去指向的时候 垃圾回收器就去回收)而消失 局部变量:随着方法的调用而存在 随着方法的调用完毕而消失 初始化不同 成员变量:有默认初始化 局部变量:...); //结果是:10 System.out.println(this.num); //结果是:0 } } 匿名对象 概念:没有名字的对象使用:...A:就用对象调用对象里面的方法一次的时候,为了节省代码 注意:调用多次的时候,不适合。

    44820

    未将对象引用设置到对象的实例–可能出现的问题总结

    7、对控件赋文本值时,值不存在。 8、使用Request.QueryString()时,所获取的对象不存在,或在值为空时未赋初始值。 9、使用FindControl时,控件不存在却没有做预处理。...,或直接用response.write()输出所取的变量值   (2)查看代码中是否存在未初始化的变量 三、 SqlConnection.Open   未将对象引用设置到对象的实例 在使用VS2003...依据google出来的结果,提到对象重定义会引起这种结果。依据这一提示,替换掉页面级全局对象,总算搞定了。       ...结果就因它而搞了几个小时。...后来依据google的结果,将不同方法中使用到的DBOper对象一一重定义为局部变量(一个方法里面仅仅定义一个DBOper对象,多次调用其方法均正常),去掉全局的private变量。

    4.4K30

    Java编程基础阶段笔记 day 07 面向对象编程(上)

    面向对象三条学习主线 面向过程 VS 面向对象 类和对象 创建对象例子 面向对象的内存分析 类的属性:成员变量 成员变量 VS 局部变量 类的方法 方法的重载 可变个数形参 面向对象:封装性 访问权限修饰符...(创建对象) 3.调用对象中的属性和方法。...当修改其中一个对象的属性的值后,其它对象的该属性不会受到影响 类的成员之 : 属性(成员变量) 变量的分类: ①按照数据类型分 :基本数据类型 vs 引用数据类型 ②按照位置分 : 成员变量  vs 局部变量...允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。...封装性的体现(狭义上): ①将属性私有化。(private修饰属性) ②提供公共的set/get方法。

    71300

    《重构》第六章 - 读后感(函数重构的9种方法)

    1.3局部变量的二次赋值 如果我们需要提炼的函数有局部变量,并且涉及的局部变量通过新函数的执行要再次将值返回去,并作为后继运算的参数。那么就需要将局部变量的计算结果返回。 除此之外,在末尾处。...那么将这个表达式提炼到一个独立的函数中,将这个临时变量的所有引用都替换为新函数的调用,这样新函数就可以被其他函数使用。...在针对具体的开发过程中,可以先对临时变量的访问符号设置为final,然后编译查看是否只有一次赋值,因为在代码中有存在多次赋值的情况,多次赋值就需要拆解成不同的变量。不要有临时变量的复用。...5.引入解释性变量 将复杂表达式的结果放进一个临时变量中,并用这个临时表达式的名称来解释表达式的用途。这个解释已经很明确了,但是复杂的表示是否有一个程度。...6.分解临时变量 如果临时变量被多次赋值,它既不是循环变量也不是用于收集计算结果,那么针对每次赋值,创造一个独立、对应的临时变量。

    70720

    未将对象引用设置到对象的实例–可能出现的问题总结

    7、对控件赋文本值时,值不存在。 8、使用Request.QueryString()时,所获取的对象不存在,或在值为空时未赋初始值。 9、使用FindControl时,控件不存在却没有做预处理。...,或直接用response.write()输出所取的变量值   (2)查看代码中是否存在未初始化的变量 三、 SqlConnection.Open   未将对象引用设置到对象的实例 在使用VS2003...依据google出来的结果,提到对象重定义会引起这种结果。依据这一提示,替换掉页面级全局对象,总算搞定了。       ...结果就因它而搞了几个小时。...后来依据google的结果,将不同方法中使用到的DBOper对象一一重定义为局部变量(一个方法里面仅仅定义一个DBOper对象,多次调用其方法均正常),去掉全局的private变量。

    5.9K10

    计算机萌新的成长历程——初识C语言4

    全局变量与局部变量的变量名可以相同,而且能同时存在;但是,在程序执行时会优先执行局部变量,如下图 所以建议是两个变量名不要重复,否则会让计算机误会,从而产生bug Q6:局部变量和全局变量的范围如何定义...的值打印成字符‘c=’加“整数数值” return 0; } 正常运行代码后,输入数值123与数值456,结果如下图所示: 这里有以下几点需要展开说明: 1.scanf(“%d%d” , &a ,...这里我将123赋值给变量a,函数正常运行后输入数值789结果如下图: 由此可知在输入函数中,我们输入的值会直接赋值给对应变量然后继续运行。...2.因为我使用的版本是VS2019,在其他VS版本中以我这种格式的代码运行,系统可能会报错,这里只需要把int c = 0;这行代码提到scanf函数前面就行,代码如下 #define _CRT_SECURE_NO_WARNINGS...3.这个问题是我自己在使用VS2019时遇到的,在VS2019版本中你正常打开程序的话是没有第一行代码“#define _CRT_SECURE_NO_WARNINGS 1”,之后运行程序的话,系统会报错

    37620

    图解python | 函数

    函数是Python中「封装可复用逻辑的代码块」,核心价值是「模块化、可复用、易维护」。本文通过图解+代码,从函数的基本结构、执行流程、核心特性到高级用法,全方位拆解Python函数的本质与用法。...3+5=8,赋值给result4.returnresult→将8返回给调用处5.调用处将返回值8赋值给变量total代码验证:python运行展开代码语言:TXTAI代码解释#定义函数defget_sum...1.形参vs实参(图解)plaintext展开代码语言:TXTAI代码解释defget_sum(a,b):#a、b是形参(形式参数,定义时的占位符)returna+btotal=get_sum(3,5)...Bob")#输出:姓名:Bob,年龄:18print_info("Bob",22)#输出:姓名:Bob,年龄:22⚠️注意:默认参数建议使用「不可变类型(如数字、字符串、元组)」,避免用列表/字典(否则多次调用会累积修改...,易维护│├─────────────┼─────────────────────────────────┤│可复用│一次定义,多次调用,减少重复代码│├─────────────┼──────────

    21610

    C++学习总结1——几个基本概念

    声明和定义 声明(declaration) 声明用于向编译器表明变量,函数或类的类型和名字,并不会为其申请存储空间,只是向程序表明了这个对象的存在。...如果多次定义,会出现重复定义(redefinition)的错误。 如果声明时有初始化式,则该声明也是定义。...对全局变量和局部变量的默认初始化规则不同,归根结底是因为它们保存的位置不同。...全局变量保存在全局数据区,该区域的变量在编译时会自动初始化;对于局部变量,系统启动时不会为其开辟内存空间,只有当它所在的函数被调用时,才在栈中建立函数数据空间。变量如果没有显式初始化,则其值为随机值。...因为C++中类会自动地增加一个默认构造函数,所以这种情况比较少见。 参考内容 1.《C++ Primer第4版》 2.Declare vs Define in C and C++

    71320

    《C语言深度剖析》---------关键字(1)

    (2)因为内存里面的数据要被临时存储,所以有了变量的存在; (3)声明和定义:定义变量,定义只能定义一次,变量的定义是要开辟空间的;而声明相当于一个告知的作用,可以进行多次的声明; 3.生命周期&&作用域...关键字一般只修饰局部变量,而且一般省略不写,这个关键字已经很老套了,一般不使用; 5.最快的关键字-----register 寄存器,cache属于CPU范畴,距离CPU越近,代价成本越高,存储本身是存在分级的...我们可以尝试在vs里面新建2个.c文件,如果我们在文件1里面定义一个函数,我们在文件2里面不进行声明也是可以调用的,这个时候编译器警告,但是能够运行; 但是如果我们在文件1里面定义一个变量,这个时候就不能够在文件...,作用域(作用的范围)还是只在函数的内部; 没有static修饰: 这个时候调用函数,创建变量,开辟空间,调用函数完成以后,这块空间就会被销毁,相当于i每一次都是从0开始进行加加; 加上static修饰之后...: 局部变量具有了全局性,每次函数的调用完成以后就不会被销毁变量空间,因此i能够从0开始,循环输出知道10停止; (6)C程序地址空间 这个我们只需要理解为什么static修饰局部变量以后,他的生命周期具有了全局性

    15410
    领券