【C++】 初阶篇 类与对象(上) 类和this指针

发布时间:2026/9/14 15:54:17

【C++】 初阶篇 类与对象(上) 类和this指针 前言从本篇开始我们将正式踏入 C 面向对象编程的核心。讲解类与对象的基础知识涵盖类的定义、访问控制、对象内存布局以及this指针等关键概念同时初次接触面向对象三大特性之一——封装为后续的深入学习打下坚实基础。一、 面向对象VS面向过程C语言是面向过程的关注的是过程分析出求解问题的步骤通过函数调用逐步解决问题。C是基于面向对象的关注的是对象将一件事情拆分成不同的对象靠对象之间的交互完成。1.1 面向过程与面向对象的区别比如 “洗衣服” 这个需求C 语言的思路是拆成 “线性步骤”用函数一步步实现// 伪代码面向过程的洗衣服void拿盆子(){...}void放水(){...}void放衣服(){...}void手搓(){...}void拧干(){...}void晾衣服(){...}// 主逻辑按顺序调用函数intmain(){拿盆子();放水();放衣服();手搓();拧干();晾衣服();return0;}核心是 “关注过程”函数是操作的载体数据比如衣服、水和函数是分离的。而 会先拆分出对象衣服、洗衣机、洗衣粉再定义每个对象的 “属性” 和 “行为”最后靠对象交互完成需求衣服对象属性脏污程度、材质行为被清洗、被晾干洗衣机对象属性容量、转速行为注水、洗衣、甩干洗衣粉对象属性类型、用量行为溶解、去污核心是 “关注对象”把数据和操作数据的方法 “打包” 到对象里逻辑更贴近现实世界也更适合复杂系统。二、 类的定义2.1引入C 语言的结构体只能放变量但 C 的结构体甚至更常用的class能同时放变量和函数—— 这是 C 实现 “数据 方法封装” 的基础。比如之前用C语言方式实现的栈结构体中只能定义变量现在以C方式实现会发现struct中也可以定义函数。typedefintDataType;structStack{// 成员函数操作栈的方法voidInit(size_t capacity){_array(DataType*)malloc(sizeof(DataType)*capacity);if(nullptr_array){perror(malloc失败);return;}_capacitycapacity;_size0;}voidPush(constDataTypedata){// 扩容逻辑简化_array[_size]data;_size;}DataTypeTop(){return_array[_size-1];}voidDestroy(){if(_array){free(_array);_arraynullptr;_capacity0;_size0;}}// 成员变量栈的数据DataType*_array;size_t _capacity;size_t _size;};// 使用时直接调用对象的方法不用传结构体指针intmain(){Stack s;s.Init(10);// 直接调用成员函数s.Push(1);s.Push(2);couts.Top()endl;// 输出2s.Destroy();return0;}不过在 C 里更推荐用class代替struct定义类 —— 两者的核心区别是默认访问权限2.2类访问限定符C 一种实现封装的方式用类将对象的属性与方法结合在一起 一般成员变量都会被限制为private/protected需要给别人使用的成员函数会放为public。限定符作用域内访问类外访问说明public✅✅对外暴露的接口如成员函数protected✅❌目前和private一样到继承那会有区别private✅❌隐藏的内部细节如成员变量public修饰的成员在类外可以直接被访问protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止如果后面没有访问限定符作用域就到 } 即类结束。class的默认访问权限为privatestruct为public(因为struct要兼容C)2.3定义类的定义和和C语言中的结构体非常相似但是C中的类除了定义变量外还可以定义函数。class为定义类的关键字{}中为类的主体。一定要注意后面的分号。类体中的内容称为类的成员类中的变量称为成员变量。类中的函数称为成员函数。2.3.1类域类定义了新的作用域名为类域。同命名空间类般能解决命名冲突。类的所有成员都在类的作用域中。在类体外定义成员时需要使用::作用域操作符指明成员属于哪个类域进行优先查找。定义一个队列定义一个栈他们都有相同名称的函数方法如果没有类域的隔离就会发生冲突。classQueue{voidPush(intx){}};classStack{voidPush(intx){}类域和命名空间域只是名称隔离不影响声明周期。2.3.2定义类的两种写法定义类一般有两种写法关键看 “成员函数的定义位置”。声明 定义全在类里#includeiostreamusingnamespacestd;classDog{public://公有intAge(){returnage;}private://一般数据都很私密像我们的身份证那些都不想给别人看到charname[5];intage;};类声明放在.h文件中成员函数定义放在.cpp文件中注意在声明的地方成员函数名前需要加类名::此时定义在类里面的成员函数默认为inline。如果不希望自己的成员函数是内联函数为了解决这个问题对同文件中的函数声明定义分离。因为内联函数不能声明定义分离。Dog.h声明#definePERSON_H// 防止头文件重复包含#includeiostreamusingnamespacestd;classDog{public:intAge(){};private:charname[5];intage;};Dog.cpp实现classDog{public:intDog::Age()//{returnage;//实现}private:charname[5];intage;};2.3.3定义习惯成员变量命名规则的建议推荐加前缀如_或后缀区分相对位置:各种公司中习惯将类的方法定义在上面把类的属性定义在下面。一定要区分 “成员变量” 和 “函数形参”避免像下面这样写 “无效代码”classDate{public:voidInit(intyear){yearyear;// 到底是给成员变量赋值还是形参自己赋值}private:intyear;// 成员变量};所以我们一般这么写classDate{public:voidInit(intyear){_yearyear;// 清晰_year是成员变量year是形参}private:int_year;// 加前缀_// 或 int mYear; // 加前缀m成员的意思};2.4类的实例化用类类型创建对象的过程称为类的实例化类只是 “对象的设计图”比如 “学生信息表” 是类“张三的信息” 才是对象 ——实例化就是用类创建对象的过程。**类没有内存对象才有**类是 “建筑设计图”对象是 “按图盖的房子”—— 设计图没有实体房子才有实际空间。一个设计图也可以盖很多房子2.5如何计算类实例化出对象的大小一个类实例化出的对象可以分为2部分成员变量成员函数那我们计算大小的时候就可以分为3种情况看以下的代码// 类中既有成员变量又有成员函数classA1{public:voidf1(){}private:int_a;};// 类中仅有成员函数classA2{public:voidf2(){}};// 类中什么都没有---空类classA3{};intmain(){coutA1:sizeof(A1)endlA2:sizeof(A2)endlA2:sizeof(A3)endl;return0;}类里既有成员变量又有成员函数但计算对象大小时我们发现只算成员变量的大小那么成员函数在哪里呢空类为什么是1呢2.5.1. 为什么不算成员函数空类为1如果每个对象都存一份成员函数100 个对象就有 100 份相同的代码会浪费内存 —— 所以成员函数统一存在 “公共代码区”所有对象共享。空类为1如果一个字节都不给怎么表示类存在过呢所以这里给1字节纯粹是为了占位标识对象存在。2.5.2.成员变量的计算方式对于多个成员变量的函数我们一般采用内存对齐的方式和C语言的结构体一样进行计算的结构体的内存对齐规则第一个成员在偏移量 0 的位置其他成员对齐到 “对齐数” 的整数倍对齐数 编译器默认对齐数VS 默认是 8和成员大小的较小值整个对象的大小是 “最大对齐数” 的整数倍。如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处结构体的整体大小就是所有最大对齐数含嵌套结构体的对齐数的整数倍这里举一个例子classA{private:char_a;// 1字节对齐数1int_b;// 4字节对齐数4min(8,4)double_c;// 8字节对齐数8min(8,8)};// 计算1_a 3填充 4_b 8_c 16字节最大对齐数816是8的整数倍coutsizeof(A)endl;// 输出162.5.3.一些小问题问题 1结构体怎么对齐为什么要进行内存对齐对齐规则 上面有对齐原因平台原因(移植原因) 不是所有的硬件平台都能访问任意地址上的任意数据的某些硬件平台只能在某些地址处取某些特定 类型的数据否则抛出硬件异常。数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于为了访问未对⻬的内存处理器需要 作两次内存访问⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节则地 址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数那么就可以 ⽤⼀个内存操作来读或者写值了。否则我们可能需要执⾏两次内存访问因为对象可能被分放在两 个8字节内存块中。问题 2如何让结构体按照指定的对齐参数进行对齐能否按照 3、4、5 即任意字节对齐指定对齐方式编译器指令如#pragma pack(n)n为对齐字节数如#pragma pack(4)指定 4 字节对齐C11 属性[[gnu::aligned(n)]]GCC 扩展或alignas(n)标准语法。后面学的可以先了解能否任意对齐不能。对齐参数n需是 2 的幂如 1、2、4、8 等因为硬件访问机制基于 2 的幂对齐非 2 幂如 3、5的对齐参数编译器可能不支持或引发性能 / 兼容性问题。问题 3什么是大小端如何测试某台机器是大端还是小端.大小端定义大端数据高位字节存低地址如0x12345678存为0x00低地址、0x0003高地址小端数据低位字节存低地址如0x1234存为0x34低地址、0x12高地址。测试方法inta1;// 二进制0x00000001if(*(char*)a1)printf(小端\n);elseprintf(大端\n);三、this 指针3.1 this 指针的引出日期类例子classDate{public:voidInit(intyear){_yearyear;}voidprint(){coutprintf()endl;}int_year;};intmain(){Date d1;Date d2;return0;}上面的代码中Date类中有 Init 与 Print 两个成员函数函数体中没有关于不同对象的区分那当d1调用Init和Print函数时该函数是如何知道应该访问的是d1对象还是d2对象呢那么这里就要看到C给了一个隐含的this指针解决这里的问题3.2定义编译器编译后类的成员函数默认都会在形参第一个位置增加一个当前类类型的指针叫做this指针this传递的是调用这个函数的对象的指针在调用的时候他会悄悄的把调用者的地址传过去。比如Date类的Init的真实原型为// 编译器的隐藏操作用户不用写voidInit(Date*this,intyear,intmonth,intday){this-_yearyear;// 通过this指针访问当前对象的成员this-_monthmonth;this-_dayday;}所以你的函数的第一个参数有没有参数他们都有一个隐含的参数this,所有的非静态的成员函数都会增加这么一个隐含的this指针。以d1举例this传过去的是d1的指针。 那么怎么知道后面的参数是d1的年月日呢this指向调用对象类的成员函数中访问成员变量本质都是通过this指针访问的如Init函数中给_year赋值this-_yearyear;注意事项C规定不能在实参和形参的位置显示的写this指针(编译时编译器会处理)但是可以在函数体内显示使用this指针。3.3特性类型类型* const即指向当前类对象的常量指针 this的值是不可以修改的this的指向的内容是可以被修改。存储与传递通过寄存器隐式传递经常使用利用寄存器存储快是成员函数的隐式形参不存储在对象内部空指针场景是否崩溃取决于是否解引用 this。若成员函数不访问成员变量未解引用 thisthis 为空时可正常运行若访问成员变量解引用 thisthis 为空则会触发未定义行为通常崩溃。// 例1不访问成员变量this为空也运行classA{public:voidPrint(){coutPrint()endl;}private:int_a;};intmain(){A*pnullptr;p-Print();// 正常运行Print()不访问_a不用解引用thisreturn0;}// 例2访问成员变量this为空崩溃classA{public:voidPrintA(){cout_aendl;}// 要访问_a需解引用thisprivate:int_a;};intmain(){A*pnullptr;p-PrintA();// 崩溃this是nullptr解引用报错return0;}cout_aendl实际上是cout_aendl解引用了。
延伸阅读

更多相关文章

2026/9/10 9:53:57

YOLOv11涨点改进| CVPR 2026 | 独家Conv创新改进篇 | 引入MBFE多分支特征增强模块,助力无人机航拍、遥感影像、小目标检测、语义分割、实例分割、目标跟踪任务,有效涨点

一、本文介绍 🔥本文给大家介绍使用 MBFE多分支特征增强模块 改进YOLOv11网络模型,MBFE将输入特征划分为增强分支和恒等分支,并通过像素级深度卷积、不同尺度的空间通道选择单元、频率成分选择单元及残差连接进行并行增强,从而联合提取目标的细粒度空间信息、多尺度局部语…

2026/9/14 22:56:04

AR远程协助可视化技术解析与应用实践

1. AR远程协助中的可视化价值解析在工业维修、医疗手术指导、设备操作培训等专业领域,AR远程协助系统正逐步取代传统的语音通话和二维视频支持。这套系统的核心突破点在于:通过虚实融合的可视化界面,将专家视角的操作指令直接叠加在真实场景中…

2026/9/14 22:56:04

Neo级轻薄本:手机SoC重构PC使用逻辑的技术解析

1. 为什么“Neo级轻薄本”突然火了?——不是参数升级,而是使用逻辑的彻底重写最近在数码圈里,“Neo级轻薄本”这个说法高频出现,但翻遍所有厂商官网、产品页、发布会PPT,你都找不到这个官方命名。它既不是Intel的Evo认…

2026/9/14 22:56:04

工业串口服务器选型的12项隐性指标解析

1. 为什么“串口服务器”不再是插上线就能用的傻瓜设备——从NCOM622样本看工业现场的真实选型逻辑你手头那台刚拆封的32路串口服务器,通电后LED灯亮了,串口能ping通IP,Modbus Poll也能连上从站——恭喜,你完成了出厂验收的前30秒…

2026/9/14 22:51:04

Zoho Projects问题解决通知自动化方案与实践

1. 项目背景与核心需求解析在项目管理场景中,问题跟踪与解决闭环是保证交付质量的关键环节。我们经常遇到这样的痛点:开发团队在内部系统中标记了问题状态为"已解决",但外部用户(如客户、合作伙伴)却无法及时…

2026/9/14 2:17:50

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/14 0:03:22

KCF目标跟踪算法与OTB工程实现:毕业设计实战解析

简介:这是一份基于KCF核相关滤波算法、融合尺度池与抗遮挡处理的目标检测跟踪MATLAB完整源码,主要面向计算机相关专业准备毕业设计、课程设计或期末大作业的学生,也适合需要项目实战练习的初学者。源码在OTB数据集上完成验证,能够…

2026/9/14 0:03:22

语音情感识别实战:Keras实现LSTM、CNN、SVM与MLP多模型对比

简介:面向语音情感识别入门与进阶开发者,这份基于Keras的项目源码完整实现了LSTM、CNN、SVM、MLP四种模型,兼容Python3.8与Keras/TensorFlow2环境。压缩包内含49个文件,大小约70.31MB,主体包括Python脚本、yaml/json配…

2026/9/14 11:59:31

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/14 13:53:59

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/14 11:22:57

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码