
【C++对象模型&程序员的自我修养】
文章平均质量分 68
参考网易云课堂狂想老师,腾讯课堂施磊老师,侯捷老师《深度探索C++对象模型》,也包含《程序员自我修养》书中的一些学习心得
越甲八千
置身其中,全力以赴
展开
专栏收录文章
- 默认排序
- 最新发布
- 最早发布
- 最多阅读
- 最少阅读
-
C++ bind
在C++中,绑定器(Binder)是一种用于将可调用对象(如函数、函数指针、成员函数指针、lambda 表达式等)与其参数进行绑定,从而生成一个新的可调用对象的工具。它允许你将一个可调用对象和其部分或全部参数进行绑定,生成一个新的可调用对象。这个新的可调用对象可以在后续被调用,并且在调用时可以省略那些已经被预先绑定的参数。函数的第一个参数绑定为后续调用时的第二个参数,第二个参数绑定为后续调用时的第一个参数,第三个参数固定为。等,用于表示在后续调用绑定后的可调用对象时,传入的参数将被放置在相应的位置。原创 2025-02-06 13:57:38 · 315 阅读 · 0 评论 -
C++ std::bindfirst
的主要功能是将一个二元函数对象(即接受两个参数的函数对象)的第一个参数固定为一个特定的值,从而生成一个新的一元函数对象。这样在后续调用这个新的一元函数对象时,就只需要提供原来二元函数对象的第二个参数即可。,这是一个用于标记一元函数对象的基类,在 C++11 之前用于提供类型信息,方便其他标准库组件(如算法)使用。等工具提供了更强大和灵活的参数绑定功能,不再依赖于这些固定的类型定义,并且可以处理更复杂的参数绑定和占位符使用等情况。是一个模板参数,表示要绑定的二元函数对象的类型。和一个要绑定的第一个参数的值。原创 2025-02-05 15:57:35 · 659 阅读 · 0 评论 -
C++ std::move
std::move的核心思想就是利用模板类型推导和类型转换,将传入的参数强制转换为右值引用类型。这样做的目的是为了在合适的场景下触发移动语义,避免不必要的深拷贝,提高程序的性能。例如,在容器的元素插入操作中,如果使用std::move将对象转换为右值引用,就可以调用对象的移动构造函数或移动赋值运算符,从而更高效地完成元素的插入操作。原创 2025-02-05 14:39:03 · 667 阅读 · 0 评论 -
std::forward
是 C++ 标准库中定义在<utility>头文件里的一个函数模板,它主要用于实现完美转发,也就是在函数模板中能够将接收到的参数(无论是左值还是右值,以及其对应的constvolatile等属性)原封不动地转发给其他函数,保证被转发函数接收到的参数特性和原调用函数传入的参数特性完全一致。下面我们来详细解析其源码实现。原创 2025-02-05 14:21:50 · 634 阅读 · 0 评论 -
C++ 类对象传参数和返回值的构造析构调用过程
调用getobj 将实参t1传给形参t , 此时注意,实参到形参的过程是赋值还是初始化呢,答案是初始化,用一个存在的对象初始化一个新对象,那肯定是调用拷贝构造函数。返回数据,返回数据过程是这样的,在调用方的栈空间构造一个临时对象,有tmp拷贝构造完成,生成一个临时对象;6、此时已经出了getda()函数的作用域了,函数内的对象开始析构,先析构tmp , 再析构形参。用生成的临时对象给t2赋值,调用赋值运算符的重载;9、main函数执行完毕,开始析构,先析构t2 , 再析构t1。原创 2025-01-11 20:48:11 · 232 阅读 · 0 评论 -
C++编译器对于类对象的优化四
①、假如返回值是个临时变量,采用赋值的方式,首先主调函数内必须先生成一个对象用来接收返回值,此时调用一次构造函数,接收返回值采用赋值,又调用了赋值运算符的重载,赋值完成后临时对象还要析构,到此调用了三次函数。②、假如采用初始化的方式:那就相当于用临时变量生成一个新对象,注意一旦遇到这种场景,编译器就会进行优化,如下:用生成临时对象的方法构造生成新对象,整个过程只有一次构造函数调用过程,效率大大提高。到此,整个代码优化完成,由最初的11个函数调用优化到现在的只有4个函数调用。原创 2025-01-11 20:29:04 · 238 阅读 · 0 评论 -
C++编译器对于类对象的优化三
编译器直接优化成,只生成临时对象a , 假如不优化:1、先调用构造函数生成临时对象A(10),然后调用拷贝构造函数生成 A a,最后出了该语句调用析构函数析构A(10) , 这样会有三个函数调用过程,效率极其低下,编译器直接优化成:按照生成临时对象A(10)的方式直接构造生成Aa;这样只有一个函数调用过程。①、定义一个临时对象会产生构造函数,函数返回临时对象会在主调函数栈空间生成一个临时对象,这个时候会调用拷贝构造函数,接下来就会析构第一个临时对象,一大堆函数调用。原创 2025-01-11 20:28:18 · 177 阅读 · 0 评论 -
C++编译器对于类对象的优化二
①、函数参数传递的过程是赋值的过程,对象之间赋值是会产生赋值运算符的重载调用,退出函数时还会再调用一次析构函数,传引用就不存在上述函数调用。原创 2025-01-11 20:27:34 · 152 阅读 · 0 评论 -
C++编译器对于类对象的优化一
综上,一共产生了四次函数调用,因为函数调用会产生很多的堆栈,消耗资源较大,编译器进行优化后,只剩下两个函数调用,大大减少了因函数调用而产生的堆栈的开辟和回收。3、因为临时对象的生存周期就是在本行,所以本行执行完毕后会调用析构函数去析构临时对象;用临时对象生成新对象的时候,临时对象被优化掉了,直接按照生成临时对象的方法生成新对象。本行代码,若是不优化的话应该这样调用。1、test(100) , 调用构造函数,构造一个临时变量。2、调用赋值运算符的重载,将上面生成的临时变量赋给 b;原创 2025-01-11 19:49:51 · 510 阅读 · 0 评论 -
C++ 虚继承加上虚函数的类对象布局
单独的虚继承,单独的虚函数的类对象模型布局我们已经讨论过,接下来两者结合的对象布局如下,先看代码。原创 2025-01-10 22:01:31 · 393 阅读 · 0 评论 -
VS两种方法查看类对象模型
在Visual Studio中,右击项目,在属性(Properties)-> C/C++ -> 命令行(Command Line)中输入。编译通过之后,所有类对象布局都在下面显示。原创 2025-01-10 20:44:32 · 423 阅读 · 0 评论 -
C++菱形继承问题汇总及其类对象模型
菱形继承涉及到四个类,首先有一个基类,然后有两个类分别继承自这个基类,最后还有一个类同时继承前面那两个派生类,从类的继承层次图来看,形状如同菱形,所以被称为菱形继承。// 基类中可以定义一些成员变量和成员函数等// 第一个派生类继承自Base类// 第二个派生类继承自Base类// 最终的派生类同时继承自Derived1和Derived2类在这个例子中,Base是基类,Derived1和Derived2是从Base派生出来的两个类,而则是同时继承了Derived1和Derived2。原创 2025-01-10 20:24:21 · 393 阅读 · 0 评论 -
虚函数配合访问权限
以上代码,虽然子类里的show是私有的,但是在编译阶段只看到p是基类,基类里的show 是公有的,那自然语法没有任何错误,编译正常,在运行是,p->show 是根据vfptr进行查找,找到子类的show , 然后把子类show地址放在寄存器里,直接call 寄存器,;函数的私有属性,这种重写不符合多态性中虚函数重写的正确规范。运行时多态和非多态在汇编来看,最明显的差异就是,非多态直接call函数地址,多态call 寄存器里存放的地址。在C++中,对于虚函数的重写(覆盖)有访问权限相关的要求。原创 2025-01-09 14:50:23 · 388 阅读 · 0 评论 -
C++虚函数遇到默认参数
1、首先,基类指针指向派生类对象,且调用的函数为虚函数,肯定会产生多态,调用肯定是子类的show ,这毫无疑问;看以下代码,基类指针指向子类对象,产生多态,我们预期肯定是调用子类的show()那么打印出来 i 应该等于 200,实际上却打印出来 i=100。2、在编译阶段,实参入栈,原创 2025-01-09 10:56:17 · 357 阅读 · 0 评论 -
C++ vtordisp的应用场景
在 C++ 中,当涉及虚继承时,为了确保在派生类对象中能正确定位虚基类的子对象(包含虚基类的数据成员、虚函数等内容),编译器会在派生类对象的内存布局中安排虚基类表指针等相关结构来记录偏移量信息,以实现准确访问虚基类部分。在 C++ 中主要应用于处理虚继承与虚函数同时存在的复杂类层次结构场景下,保证对象构造、析构以及多态操作等过程中对虚基类的访问和相关函数调用的准确性与正确性,虽然它更多是编译器在后台处理的一种机制,但理解它的应用场景对于深入掌握 C++ 复杂的面向对象特性和编写正确的代码有着重要意义。原创 2025-01-08 22:35:41 · 920 阅读 · 0 评论 -
基类指针指向派生类对象,基类指针的首地址永远指向子类从基类继承的基类首地址
基类指针指向派生类对象,基类指针的首地址永远指向子类从基类继承的基类首地址。原创 2025-01-08 20:21:34 · 453 阅读 · 0 评论 -
详细全面讲解C++中重载、隐藏、覆盖的区别
综上所述,函数重载、覆盖和隐藏有着不同的定义、使用场景和特点,正确理解它们之间的区别对于掌握C++的面向对象编程、多态机制以及类之间的函数调用关系等方面有着重要的意义。类中重写后的版本,体现了多态性。函数时,编译器根据传入的实际参数情况来选择匹配的重载函数进行调用。类自身定义的函数,如果要访问基类中被隐藏的同名函数,需要通过。函数有多个重载形式,有的重载函数参数个数不同(如两个。参数的版本),有的重载函数参数类型不同(如。函数时,则分别调用各自类中定义的函数。函数时,编译器会认为是调用。原创 2025-01-08 19:23:14 · 1724 阅读 · 0 评论 -
带虚继承的类对象模型
vbptr是虚基类表,里面存放的是虚基类的成员变量相对于类对象的偏移量。2、将虚基类整体移到最下面,在原来虚基类的地方加上vbptr。虚继承的优先级比普通继承的优先级高。1、先按照常规布局分布内存。原创 2025-01-03 22:44:45 · 442 阅读 · 0 评论 -
常规继承类对象大小
这个相对简单,只需要考虑类对象大小的累加和内存对齐。原创 2025-01-03 21:55:19 · 235 阅读 · 0 评论 -
const 在C和C++中的区别
修饰的变量时,可能会进行一定程度的优化,比如将其值直接替换到使用它的地方(类似宏展开的效果),但由于其本质还是变量,这种优化相对有限,并且如果通过一些特殊手段(如指针操作改变其值),可能会导致优化后的结果不符合预期。修饰的全局常量,编译器可以在编译期确定其值,并将其当作真正的常量来处理,在整个程序的多处使用该常量的地方都可以直接用其值替换,而且不用担心值会被改变,这有助于提高代码的执行效率以及减少程序运行时的内存占用等情况。修饰的函数参数还涉及到函数重载等方面的影响,比如可以根据参数是否为。原创 2025-01-03 15:28:44 · 1119 阅读 · 0 评论 -
C和C++相互调用详解
由于C和C++代码在编译时生成的符号不同,而我们经常会在C代码里调用C++的代码,或者在C++代码里调用C的代码,下面就简单总结一下二者相互调用时的语法。最主要的就是在C++代码里添加 extern “C” 1、首先C代码调用C++ main.c#includeint sum(int a,int b);//此时sum函数生成的符号是C规则下的符号,int main(){原创 2017-11-04 14:29:42 · 1394 阅读 · 0 评论 -
C代码内存区域划分
3、编译阶段未初始化的全局变量放在COM块,因为未初始化的全局变量为弱符号,要经过强弱符号的选择之后,才能确定具体选择的变量。2、初始化为0,和未初始化的(全局变量、静态全局变量和静态局部变量)放在.bss。1、初始化不为零的(全局变量、静态全局变量和静态局部变量)放在.data段。4、链接完成后未初始化的全局变量放在.bss段。5、.text存放指令。原创 2023-11-03 09:06:02 · 628 阅读 · 0 评论 -
编译链接全过程
在汇编语言文件中可能存在一些符号(比如变量名、函数名等),汇编器会给这些符号分配相应的内存地址(相对地址或者绝对地址,取决于具体情况),并将汇编指令中涉及到这些符号的地方用对应的地址进行替换,使得最终生成的机器码能够准确地在内存中找到相应的操作对象,例如在汇编语言中出现的变量。不同的目标文件(可能来自不同的源文件编译、汇编后生成)中可能存在相互引用的符号,比如一个源文件中的函数调用了另一个源文件中定义的函数,在目标文件阶段这些符号只是一个未确定的引用,链接器要做的就是找到这些符号对应的实际定义所在的位置。原创 2025-01-02 21:09:28 · 1077 阅读 · 0 评论 -
C语言中的强弱符号
强符号强符号通常是指在编译单元(一般是一个源文件,即.c文件)中定义的全局变量或者函数。例如,在一个.c文件中定义了如下全局变量:// 这是一个强符号的全局变量定义} // 这是一个强符号的函数定义强符号代表了一个确切的、具有唯一内存地址的实体,在链接阶段会参与到最终可执行文件的符号整合过程中。弱符号弱符号相对特殊,它也是一种全局变量或者函数的表示形式,但允许在链接阶段被其他同名的强符号覆盖。原创 2025-01-02 20:21:37 · 1348 阅读 · 0 评论 -
C++类对象内存对齐方式
内存对齐(Memory Alignment)也叫字节对齐,是指数据在内存中的存放地址要满足一定的规则,通常是按照数据类型自身的对齐模数来进行对齐放置。这样做的主要目的是为了提高处理器访问内存的效率,因为处理器在读取内存时往往按照字长(比如32位系统按4字节,64位系统按8字节)等单位进行读取,合适的对齐能让数据一次性被完整读取,减少读取次数。整个类对象的大小可能会是10字节,但考虑到内存对齐,编译器通常会填充到12字节,使得下一个对象(如果有连续存放的情况)也能按照合适的对齐要求来放置。原创 2025-01-02 17:12:18 · 845 阅读 · 0 评论 -
C++ 空类大小
而且在涉及到类的继承、多态等更复杂的面向对象特性时,空类作为一种基础的类定义形式,其大小设定为1字节也便于在后续扩展和构建更复杂的类层次结构时,能和其他有成员的类在内存布局、对象操作等方面保持协调统一,避免因为特殊的大小设定(比如0字节)而带来各种难以处理的兼容性问题和不符合常规编程预期的情况。通过将空类的大小设定为1字节,每个对象在内存中都有了自己独立的、哪怕是最小限度的空间,编译器就能为它们分配不同的内存地址,从而保证了对象之间的可区分性,使得程序能够像对待其他正常有成员的类对象一样去操作它们。原创 2025-01-02 15:47:59 · 927 阅读 · 0 评论