新闻详情

新闻详情

首页 / 资讯中心 / 详情

从结构体到对象:正式学习C++类的骨架、封装与this指针

发布时间:2026/9/27 9:39:19来源:尧图网络
从结构体到对象:正式学习C++类的骨架、封装与this指针
文章目录1. C在C后的延伸2. 类的基本骨架成员、类域和定义位置2. 类定义中放什么2.2 类内定义与类外定义2.3 class 和 struct 的差别3. 访问限定符封装不是仅仅把数据藏起来3.1 public 和 private3.2 protected 先知晓边界即可4. 对象大小成员变量属于对象成员函数不按对象重复保存5. this 指针成员函数如何找到当前对象5.1 从两个对象调用同一个函数5.2 this 的几个性质5.3 空指针调用成员函数为什么有时不马上崩溃7. 小结1. C在C后的延伸C在保留了大量C的表达方式的同时增加了更强的类型组织和抽象工具一些数据与操作数据的函数可以被组织到同一个类中这正是C面向对象的开始。在C中我们可能这样表示一个日期typedefstructDate{intyear;intmonth;intday;}Date;voidDateInit(Date*d,intyear,intmonth,intday){d-yearyear;d-monthmonth;d-dayday;}voidDatePrint(constDate*d){printf(%d/%d/%d\n,d-year,d-month,d-day);}调用时必须把对象地址显式传给每一个函数Date d;DateInit(d,2026,9,24);DatePrint(d);这种写法可以工作但是它把“哪些函数属于Date”交给了命名习惯来维护。函数写错参数、直接修改不应该暴露的数据与忘记检查日期等等操作是否合乎规则都只能依靠调用者自觉安全性较低。而C类则把这层关系写进类型本身classDate{public:voidInit(intyear,intmonth,intday);voidPrint()const;private:int_year0;int_month0;int_day0;};调用时变成Date d;d.Init(2026,9,24);d.Print();2. 类的基本骨架成员、类域和定义位置2. 类定义中放什么classCounter{public:voidAdd(intvalue){_valuevalue;}intGet()const{return_value;}private:int_value0;};这里定义的是一个计数器对象其拥有其的私有成员变量_value然后拥有两个成员函数一个用于获取当前计数一个用于增加指定数量。class Counter { ... };中的内容叫类体。类体里的变量叫成员变量函数叫成员函数。类定义最后的分号不能省略因为整个类定义在语法上也是一条声明。下面我们创建两个对象first和second让其增加不同的计数Counter first;Counter second;first.Add(3);second.Add(10);std::coutfirst当前计数first.Get()std::endl;std::coutsecond当前计数second.Get()std::endl;运行示例可以看到first和second是两个对象各自拥有一份_value。2.2 类内定义与类外定义代码量较少的成员函数可以直接写在类内较长的函数则通常在类外定义让类体更像一份接口说明classCounter{public:voidAdd(intvalue);intGet()const;private:int_value0;};voidCounter::Add(intvalue){_valuevalue;}intCounter::Get()const{return_value;}Counter::Add中的::表示“到Counter这个类域中寻找Add”。如果省略Counter::编译器会把它当成普通的全局函数无法直接访问_value。对于成员函数的函数头后加的const表示的是此函数不会修改对象的相关成员变量是保护数据的一种方式如果函数不需要修改成员变量的话建议加上增加代码可靠性。2.3 class 和 struct 的差别C 中的 struct 也可以包含成员函数和访问限定符structPoint{voidPrint()const;int_x0;int_y0;};但两者的主要默认规则不同class 的默认访问权限是 privatestruct 的默认访问权限是 public。C 中可以继续使用struct表示简单数据也可以使用class表示需要封装行为的类型。关键不在于关键字哪个“更高级”而在于你是否清楚哪些成员应该暴露给外部struct适合纯数据载体类似 C 结构体数据默认公开简单方便class适合需要封装的类型内部数据默认私有控制对外接口。3. 访问限定符封装不是仅仅把数据藏起来3.1 public 和 privateclassCounter{public:voidAdd(intvalue){if(value0){_valuevalue;}}intGet()const{return_value;}private:int_value0;};当外部想要使用成员变量_value时只能通过 Add 和 Get 使用SafeCounter counter;counter.Add(5);counter._value-100;// 编译错误_value 是 private封装的价值是把规则集中在类内部。以后如果计数器需要限制最大值只改Add就可以调用者不需要知道_value的具体表示方式。访问限定符的作用范围从它出现的位置开始一直持续到下一个访问限定符或类定义结束默认是privateclassExample{int_privateValue0;// 默认 privatepublic:voidSet(intvalue){_privateValuevalue;}private:int_anotherValue0;};3.2 protected 先知晓边界即可protected 在类外不能直接访问和 private 在当前阶段的表现相同。它主要会在继承中体现差异。开始学习类和对象时只需要重点明白需要对外提供的操作放到 public表示细节的数据和辅助函数通常放到 private。4. 对象大小成员变量属于对象成员函数不按对象重复保存观察下面的类型classSmall{public:voidPrint()const{}private:int_value0;};classEmpty{public:voidPrint()const{}};第一个Small类中有一个成员变量而Empty中无成员变量让我们看看其各自的对象大小std::coutSmall类大小sizeof(Small)std::endl;std::coutEmpty类大小sizeof(Empty)std::endl;运行示例可以看到Small至少需要容纳一个int(4个字节)。Empty没有成员变量通常仍然占 1 个字节用来区分不同对象的地址。成员函数编译后是指令不需要在每个对象里复制一份因此一般不计入对象大小。这就引出了成员变量与成员函数的不同存放方式成员变量 vs 成员函数成员变量每个对象独立一份其对应内部的成员变量有各自的内存空间。成员函数整个程序里只有一份所有对象共用。成员函数的代码通常保存一份调用时会根据对象不同而操作对应的成员变量。其内部会依靠编译器隐式传入的this指针实现。类的大小实际和C中结构体相似还是会受到内存对齐影响规则类似第一个成员通常从对象起始地址开始后续成员可能为了满足对齐要求产生填充sizeof是编译器根据布局规则计算出的结果不要只用成员大小简单相加。5. this 指针成员函数如何找到当前对象5.1 从两个对象调用同一个函数classDate{public:voidInit(intyear,intmonth,intday){_yearyear;_monthmonth;_dayday;}voidPrint()const{std::cout_year/_month/_daystd::endl;}private:int_year0;int_month0;int_day0;};intmain(){Date d1;Date d2;d1.Init(2026,9,24);d2.Init(2027,1,1);d1.Print();d2.Print();}Init的函数体没有写d1或d2但两次调用结果不同。原因就是是非静态成员函数会有一个编译器隐含传入的参数this。可以把调用过程近似理解为d1.Init(2026, 9, 24) - 类似 Init(d1, 2026, 9, 24) d2.Init(2027, 1, 1); - 类似 Init(d2, 2027, 1, 1)在真实的 C 语法中我们不需要在调用时手写这个参数但成员函数内部确实可以使用thisvoidDate::Init(intyear,intmonth,intday){this-_yearyear;this-_monthmonth;this-_dayday;}在编写时通常可以省略this-因为编译器知道_year是当前对象的成员。显式写出this指针常用于成员变量和参数同名时或者是为了强调当前对象。5.2this的几个性质this 是成员函数内部可用的指针它指向调用当前成员函数的对象不能修改 this 让它指向另一个对象静态成员函数没有 this因为它不依附于某个具体对象。5.3 空指针调用成员函数为什么有时不马上崩溃观察并运行以下代码classDemo{public:voidHello()const{std::couthellostd::endl;}voidPrintValue()const{std::cout_valuestd::endl;}private:int_value10;};intmain(){Demo*ptrnullptr;ptr-Hello();// 可能看起来能运行ptr-PrintValue();// 访问成员数据通常会崩溃}运行示例从语言规则上说通过空指针调用非静态成员函数本身就是未定义行为不能因为某次运行没有崩溃就认为它是安全的。PrintValue需要通过this找到_value所以更容易触发崩溃。注意不要把这种使用方式当作技巧而是通过这个例子帮助我们理解this指针的存在与作用。7. 小结类不是把 C 语法换一种写法而是把数据、操作和访问边界组织到一个类型中。对象拥有自己的状态成员函数通过隐含的 this 找到当前对象访问限定符则决定外部能否直接触碰这些状态。理解这三层关系后构造函数、析构函数和运算符重载就不再是孤立语法而是继续完善“对象类型”的工具。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

ESP32-CAM图像传输实战:硬件接线、源码与踩坑全解析 2026/9/27 10:32:47

ESP32-CAM图像传输实战:硬件接线、源码与踩坑全解析

1. 项目概述:为什么ESP32-CAM图像传输值得你花三小时认真读完我第一次把ESP32-CAM连上电脑,看到串口监视器里跳出“Camera init failed”时,手里的杜邦线差点被捏断。那会儿网上搜“ESP32-CAM 图像传输”,前五页全是复制粘贴的Ard…

阅读更多 →
深圳中小企业网站建设避坑指南:5个步骤搞定不拖沓 2026/9/27 10:32:40

深圳中小企业网站建设避坑指南:5个步骤搞定不拖沓

深圳中小企业网站建设避坑指南:5个步骤搞定不拖沓 改个按钮位置,建站公司让你等一周?后台改个文案,还要走一遍“需求变更流程”?在深圳做老板的,最怕的就是这种“大爷式”服务。你急着要上线引流,对方却按部就班,效率低到让人想砸电脑。其实,深圳中…

阅读更多 →
I2C、I2S、SPI、UART通信协议选型指南:从原理到实战 2026/9/27 10:32:40

I2C、I2S、SPI、UART通信协议选型指南:从原理到实战

1. 四种通信协议到底该怎么选:从踩坑现场说起i2c、i2s、spi、uart这四个词,凡是摸过单片机、写过驱动、调过传感器的人,几乎每天都在跟它们打交道。但真要让人用一句话说清楚“什么时候该用哪个”,很多人还是会卡壳。我自己在早期…

阅读更多 →
ESP32 -O2崩溃根源与实战排查:volatile、ISR、寄存器提升三大陷阱 2026/9/27 10:32:19

ESP32 -O2崩溃根源与实战排查:volatile、ISR、寄存器提升三大陷阱

1. 问题本质:不是编译器“变坏了”,而是代码里藏着没被发现的定时炸弹嵌入式ESP32开发中,把编译优化等级从-g -Og或-g -O0(俗称-debug模式)切换到-O2后程序直接崩溃——这绝不是ESP32芯片突然失灵,也不是ES…

阅读更多 →
3个坑别踩!合肥市门窗工程在哪个网站接活做怎么选 2026/9/27 10:32:08

3个坑别踩!合肥市门窗工程在哪个网站接活做怎么选

3个坑别踩!合肥市门窗工程在哪个网站接活做怎么选 做门窗工程的老板们,是不是常被那种套壳模板网站恶心到了?页面丑得没法看,加载还慢,客户点进来两秒就关了。这种站不仅留不住人,更别提转化订单了。很多人都在搜“合肥市门窗工程在哪个网站接活做怎么…

阅读更多 →
自己不会代码想建站?一文搞懂免费网页代码大全实操指南 2026/9/27 10:32:08

自己不会代码想建站?一文搞懂免费网页代码大全实操指南

自己不会代码想建站?一文搞懂免费网页代码大全实操指南 想做个网站,心里没底,怕被坑,又怕花钱多。很多老板和运营人第一反应就是去找【免费网页代码大全】,想着能不能直接抄一套来用。其实,这背后反映的是“自己不会代码想做网站”的普遍焦虑。…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉