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

day 19

如何理解C++面向对象编程?

类(Class):类是面向对象编程的核心概念,它是对象的蓝图或模板。类定义了对象的属性(成员变量)和行为(成员函数)。
对象(Object):对象是类的实例。通过类创建的对象具有类定义的属性和行为。
封装(Encapsulation):封装是将数据(属性)和操作数据的方法(行为)捆绑在一起,并对外隐藏内部实现的细节。通过访问控制(如private、public、protected)来实现。
继承(Inheritance):继承允许一个类(派生类)基于另一个类(基类)来创建,继承基类的属性和行为,并可以扩展或修改它们。
多态(Polymorphism):多态允许不同类的对象对同一消息做出不同的响应。在C++中,多态通常通过虚函数(virtual)和函数重写(override)来实现。

虚函数底层机制

当通过基类指针调用虚函数时,编译器会生成以下代码:

1.通过对象的vptr找到虚函数表。
2.根据虚函数在表中的偏移量找到对应的函数地址。
3.调用该函数。
虚函数的实现引入了以下性能开销:

内存开销:每个对象需要额外存储一个vptr,每个类需要维护一个虚函数表。
时间开销:虚函数调用需要通过vptr查找虚函数表,比普通函数调用多一次间接寻址。

this指针

它是一个隐含的指针,指向当前对象的地址。
1.this的概念
this指针是一个隐含的指针,每个非静态成员函数都可以访问它。因为静态成员函数属于类不属于对象
this指针指向调用该成员函数的对象。
this指针的类型是ClassName*,其中ClassName是当前类的名称。
this指针是一个常量指针,不能修改它的值(即不能让它指向其他对象)。
2.this的作用
区分成员变量和局部变量:当成员变量与局部变量同名时,可以使用this指针明确访问成员变量。
返回当前对象:在成员函数中返回当前对象的引用或指针。
链式调用:通过返回*this,可以实现链式调用。
3.底层实现

void MyClass::setValue(int value) {
    this->value = value;
}

会被编译器转换为:

void MyClass::setValue(MyClass* this, int value) {
    this->value = value;
}

当调用obj.setValue(42)时,编译器会隐式传递obj的地址作为this指针:setValue(&obj, 42);

this存在哪
(1)寄存器
在函数调用时,this指针通常会被存储在寄存器中(如x86架构中的ECX寄存器,x64架构中的RCX寄存器)。
寄存器是CPU内部的高速存储单元,访问速度非常快。
(2)栈
如果寄存器不足以存储所有参数,this指针可能会被压入栈中。
栈是程序运行时的一块内存区域,用于存储函数调用的上下文信息(如参数、局部变量等)。
生命周期
this指针的生命周期与成员函数的调用周期相同。
struct中有this指针吗
几乎完全相同,所以有

struct和class最本质的区别

struct和class的最本质区别在于默认的访问控制权限:

struct默认是public访问权限和public继承。
class默认是private访问权限和private继承。

函数运行全过程的底层机制

1. 函数调用
调用指令:当程序执行到函数调用时,会生成一条调用指令(如 call 指令),该指令会将当前指令的下一条指令的地址(返回地址)压入栈中,然后跳转到函数的入口地址。
参数传递:函数的参数通常通过寄存器或栈传递给被调用函数。具体传递方式取决于调用约定(如 cdecl、stdcall、fastcall 等)。
2. 栈帧管理
栈帧(Stack Frame):每个函数调用都会在栈上创建一个栈帧,用于存储函数的局部变量、返回地址、保存的寄存器等信息。
栈指针(SP)和帧指针(FP):栈指针指向当前栈顶,帧指针指向当前栈帧的基址。函数调用时,栈指针会向下移动,为新的栈帧分配空间。
3. 局部变量存储
局部变量:函数的局部变量通常存储在栈帧中。编译器会根据变量的类型和大小,在栈帧中为它们分配空间,局部变量多,栈帧就大。
初始化:局部变量在进入函数时会被初始化,通常是通过将栈帧中的相应位置清零或赋初值。
4. 函数执行
指令执行:函数体中的指令会依次执行,可能会涉及到对局部变量的读写、调用其他函数、进行算术运算等操作。
寄存器使用:函数执行过程中会使用寄存器来存储临时数据、进行运算等。为了保存调用者的寄存器状态,函数通常会先将寄存器的值压入栈中,函数返回时再恢复
5. 返回值处理
返回值:函数的返回值通常通过寄存器(如 eax 在 x86 架构中)返回给调用者。如果返回值较大,可能会通过栈或内存传递。
返回指令:函数执行完毕后,会执行返回指令(如 ret 指令),该指令会从栈中弹出返回地址,并跳转到该地址继续执行。
6. 栈帧销毁
栈帧释放:函数返回后,栈指针会恢复到调用前的状态,释放当前栈帧。调用者继续执行后续指令。
7. 异常处理
异常机制:如果函数执行过程中发生异常(如除零错误、内存访问违规等),操作系统或运行时环境会捕获异常,并根据异常处理机制(如 try-catch 块)进行处理。
8. 递归调用
递归:如果函数是递归调用的,每次调用都会创建一个新的栈帧,直到递归终止条件满足。递归深度过深可能导致栈溢出。
9. 优化
内联函数:编译器可能会将小型函数内联展开,以减少函数调用的开销。
尾调用优化:如果函数的最后一步是调用另一个函数,编译器可能会进行尾调用优化,直接跳转到被调用函数,而不创建新的栈帧。

typedef和define区别

typedef 是类型定义,适合用于创建类型别名,提高代码可读性和类型安全性。
类型安全:typedef 是类型定义,编译器会进行类型检查。
作用域:typedef 的作用域遵循 C/C++ 的作用域规则(如块作用域、文件作用域等)。
#define 是文本替换,适合用于定义常量、函数宏和条件编译,但缺乏类型安全,容易引入错误。
文本替换:#define 是简单的文本替换,不涉及类型检查。
作用域:#define 的作用域从定义处开始,直到文件结束或被 #undef 取消定义。

相关文章:

  • 量化自学 - 金融理论与python - Net Present Value 净现值
  • Linux arm64 IOMMU总结
  • 【产品小白】B端产品系统从需求到落地
  • 前端对话框项目 react如何实时接收,Node.js 服务端转发Coze API响应结果详解
  • deepin 下安装nvm(npm+node)
  • 【LeetCode】LCR 139. 训练计划 I
  • SAP-工单技术性关闭操作手册
  • 【STM32学习】标准库实现STM32 ADC采集1路、2路、多路
  • JWT 令牌
  • top命令显示iowait (wa)非常高时怎么排查
  • 环境变量2
  • C# 背景 透明 抗锯齿 (效果很不错)
  • 清华大学DeepSeek最新研究报告《DeepSeek与AI幻觉》【附下载链接】
  • C++二叉树:数据的“家族树”与高效检索的奥秘
  • python subprocess库
  • 阿里云上的网站配置HTTPS
  • Ansys EMC Plus:HIRF 与飞机耦合演示
  • qsort介绍与实现
  • 【Python 学习 / 7】模块与文件操作
  • 一键部署开源DeepSeek并集成到钉钉
  • 法治课|争议中的“行人安全距离”于法无据,考量“注意义务”才更合理
  • 外交部亚洲司司长刘劲松会见印度驻华大使罗国栋
  • 王毅人民日报撰文:共商发展振兴,共建中拉命运共同体
  • 马上评丨为护士减负,不妨破除论文“硬指标”
  • 河南洛阳新安县煤渣倾倒耕地:多年难恢复,为何至今未解决?
  • 人民日报读者点题·共同关注:今天我们为什么还需要图书馆?