C++类与对象完整学习路线(上)
文章导航前序一.类的定义1.类定义格式1.1 访问限定符默认规则1.2 类域二.实例化到底是什么1.实例化2. 对象的大小三.你了解this指针多少大家好我是小沐。最近持续深耕C基础语法今天也是刚好来到了“类与对象”这一板块我也刚好整理了一些知识点对新手友好适合刚入门的同学查缺补漏。类的定义、实例化和this指针一.类的定义1.类定义格式C中用class和struct两个关键字来定义类{}中为类的主体结束时加上分号即可和C语言中的结构体写法一模一样。那我们不经问类到底是什么类是一种自定义的数据类型把自变量和函数作为主体即封装称里面的自变量为成员变量或属性称函数为成员函数或方法。那小白肯定会有疑惑的刚从C语言过渡只见过内置的数据类型如int 、char、double和struct等甚至都不知道一直在用的struct也是一种数据类型那你一个类里面又装函数又装变量的既然也是一种数据类型这时候看到这里脑子里面就会懵懵的。是的类就是一种数据类型。class zhangsan { void fuct1() { cout 1 endl; } }; struct lisi { void fuct2() { cout 2 endl; } };两种类定义和使用方法一模一样也算沿用C语言结构体的方式了。我们继续看下面这种调用会有什么结果#include iostream using namespace std; class zhangsan { void fauct1() { cout 1 endl; } }; struct lisi { void fuct2() { cout 2 endl; } }; int main() { zhangsan zhang; lisi li; zhang.fuct1(); li.fuct2(); return 0; }答案就是编译报错。class和struct定义的类虽然调用方式一样但语法上存在差异那就是访问限定符默认规则不一样。1.1 访问限定符默认规则class与struct的访问限定符——》public 公开的访问限定符——》protect 保护性有限制的限定符——》private 私有性限定符总所周知有这三个限定符修饰的区域都会有相应的特性在里面那没有限定符修饰的区域呢从上面的例子我们知道并不是两个都可以直接访问默认区域的成员函数的。规则class的默认区域是属于private区域的类外不以直接访问。struct的默认区域是属于public区域的类外可以直接访问。在习一般情况下我们是不会把成员函数写到private中而是写到public中。那么定义在private中的成员变量外边就没有办法访问了吗——是的类外确实不能直接访问private中的成员变量但可以通过这种间接的访问获取成员变量。我们以时间类为例class Date { public: int GetYear() { return _year ; } int GetMonth() { return _month; } int GetDay() { return _day; } private: int _year; int _month; int _day; };1.2 类域上面我们说类是一种数据类型那么这种数据类型定义时就会产生一个域它的所有成员都在里面我们称这个域为 “类域”。类域和其它域一样都会影响编译器的查找规则。我们一起来看下面的例子class Stack { public: //成员函数 void Init(int n 4); private: //成员变量 int* array; size_t capacity; size_t top; }; void Init(int n) { array (int*)malloc(sizeof(int) * n); if (array nullptr) { perror(Init::malloc); return; } capacity n; top 0; } int main() { Stack st; st.Init(); return 0; }思考一下这段代码有没有问题答案有问题我们说类会定义出一个域而域要被类外访问是需要通过域访问限定符::访问的。也就是说void Stack::Init(int n) //这里需要加上Stack:: { array (int*)malloc(sizeof(int) * n); if (array nullptr) { perror(Init::malloc); return; } capacity n; top 0; }如果这里没有加入Stack::这个初始化函数就只是一个在全局变量中定义的函数缺少的各种变量编译器都会自动去全局域里面找没有找到自然就会报错了。二.实例化到底是什么1.实例化定义类定义出的具体对象叫做实例化说得这么高级其实就是用类定义一个具体对象如class Data { public: int month; int day; } int main() { Data d1; //d1就是具体对象 return 0; }这里的d1就是具体的对象。定义了具体对象后开辟了可以运用的空间这个过程我们就管它就实例化。形象比喻类定义时就像做建筑用的设计图这时建筑仅仅停留在图纸层面还不能住人。当定义了一个具体对象后就像建筑已经从设计图到完成建筑建设可以住人了。2. 对象的大小思考既然类是一种数据类型那么类的大小是多大呢是所有成员的简单相加吗在计算对象大小前我们先再弄明白一个事情类的成员函数是存储在类里面吗如果是那就是定义一个具体对象就会同时在栈帧上开辟一块空间存储函数。如果不是那成员函数会存储在哪多个对象调用时过程是怎样的为回答这个问题我们将从汇编的层面观察调用过程。以这段代码为例class Stack { public: //成员函数 void Init(int n 4) { array (int*)malloc(sizeof(int) * n); if (array nullptr) { perror(Init::malloc); return; } capacity n; top 0; } private: //成员变量 int* array; size_t capacity; size_t top; }; int main() { Stack ST1; Stack ST2; ST1.Init(); ST2.Init(); return 0; }现在我们分别在ST1和ST2调用出打断点进行调试为方便观察我们选择在x86环境进行调试。我们看到第一步push入栈第二步把ST1的地址传给寄存器ecx第三步把Stack里面的Init函数的地址计算出来然后跳转到这个函数的地址执行操作。我们画的两个红圈中明显标注着ST1和ST2调用时计算出的地址是一样的呀这说明它两调用的是同一个函数也就是说类的成员函数虽然是在类里面定义的但编译时并没有存储在类的栈区里面。既然成员函数没有存储在类的栈区里面那么类的对象的大小就不需要计算成员函数的大小。好我们回归对象大小的计算问题一起看例子求下面对象的大小:class ST1 { public: int a; char b; }; class ST2 { void fuct() { int a; } }; class ST3 { }; int main() { cout sizeof(ST1) endl; cout sizeof(ST2) endl; cout sizeof(ST3) endl; return 0; }请问这段代码输出的结果是多少A.50, 0 B.8 0, 0 C.5,1,1 D.8,1,1我们来公布答案由于编译器在运行时受到内存对齐规则的约束第一个成员放在结构体偏移量为0的位置其它成员对齐到对齐数vs的默认对齐数为8的整数倍的地址出。注对齐数 编译器默认的一个对齐数 与该成员大小的较小值。总大小 最大对齐数的整数倍那么ST1在内存中应该是这样的由于最大对齐数是整型的对齐数4故总大小应该为4的整数倍故最终结果应为8个字节。问为什么只定义了成员函数和没有成员的对象大小不为0因为如果一个字节都不给就没有办法表示它们确实存在所以这里给1字节纯粹是为了占位标识对象存在。那么问题又来了为什么要内存对齐这不是会造成内存浪费吗这和机器硬件层面有关硬件里面有一个叫做地址总线有32条或64条与机器有关每一条通过充放电表示1和0也就是2进制的表现形式。以32条地址总线的机器为例无论你数据在内存中如何存放我cpu每次读取都是按照32bit的大小的整数倍范围进行读取如果数据存放没有内存对齐有可能出现数据被截断的情况就需要在读取结束后进行拼接更加繁琐还导致性能下降。这个时候我们的选择是牺牲空间换取时间。三.你了解this指针多少上面我们有说过类的成员函数并存储在类的栈帧区域里面但一个类同时定义出多个具体对象时它们可以调用统一函数那么这个函数是如何识别是哪个具体对象在调用它呢答案就是这部分的主角this指针。class Date { public: void DATE(int year, int month, int day) //void DATE(DATE* const this, int year, int month, int day) { cout year / month / day endl; } }; int main() { Date d1; Date d2; d1.DATE(2026, 8, 3); //d1.DATE(d1,2026, 8, 3); d2.DATE(2023, 9, 1); //d2.DATE(d2,2023, 9, 1); return 0; }在类域里面定义的函数都隐藏了一个this指针,在调用函数时编译器会自动帮你把对象的地址也传过去在输出部分实际上是这样的void DATE(int year, int month, int day) //void DATE(DATE* const this, int year, int month, int day) { cout this-year / this-month / this-day endl; }只是编译器把this指针给隐藏了。C规定不能在实参和形参的位置显式的写this指针但是可以在函数体内部显式写this指针。既然是指针就有可能出现空指针解引用的情况现在我们结合前面的知识来判断下面两段代码运行结果是什么代码1class A { public: void Print() { cout Print(int a 4) endl; } private: int _a; }; class B { public: void Print() { cout Print(int a 4) endl; cout _a endl; } private: int _a; }; int main() { A* a nullptr; //B* b nullptr; a-Print(); //b-Print(); return 0; }代码2class A { public: void Print() { cout Print(int a 4) endl; } private: int _a; }; class B { public: void Print() { cout Print(int a 4) endl; cout _a endl; } private: int _a; }; int main() { //A* a nullptr; B* b nullptr; //a-Print(); b-Print(); return 0; }代码1编译报错 运行崩溃 正常运行代码2编译报错 运行崩溃 正常运行那么答案是代码1正常运行代码2运行崩溃。如果这个问题没有做对说明你对this指针还是不了解。知识点this指针传的是对象的地址不是对象指针的地址这两个定义因为a和b已经存放的是对象的地址了所以这里传值的时候默认是这样a-Print(a);//不是a-Print(a) b-Print(b);//不是b-Print(b)那我们又知道地址传过去后如果没有解引用操作编译器是不会报错的。而代码1仅仅是打印了一个字符串啊并没有解引用操作。 对于代码2就不一样了class B { public: void Print() { cout Print(int a 4) endl; cout _a endl; //这里本质就是this-_a } private: int _a; };这里面_a的打印实际就是this-_a但是我们的this指针传过去的是空指针啊对空指针解引用就导致程序崩溃。好了本篇到这里就结束了你在学习这部分的时候有没有踩过什么坑评论区聊聊我整理到下一篇里~

相关新闻

从别名到脚本:构建高效个人命令管理体系的完整指南

从别名到脚本:构建高效个人命令管理体系的完整指南

1. 从“无法识别”到高效助手:为什么你需要一个命令管理器如果你在Windows的PowerShell里敲下opencode,大概率会看到那个熟悉的红色错误:“无法将‘opencode’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”。这个错误本身&#xff0c…

2026/10/14 6:04:57 阅读更多 →
AI如何真正处理数学问题:从文本理解到定理证明的工程实践

AI如何真正处理数学问题:从文本理解到定理证明的工程实践

最近在技术社区里,一个标题为“OpenAI Astra 破解数学十大难题”的消息引起了不小的讨论。乍一看,这像是一个激动人心的突破,仿佛AI即将攻克人类智慧的巅峰。但作为一名长期与各类AI工具和模型打交道的开发者,我的第一反应是&…

2026/10/14 6:05:15 阅读更多 →
微信ClawBot技术解析:开源AI模型与微信生态的平民化AI代理实践

微信ClawBot技术解析:开源AI模型与微信生态的平民化AI代理实践

1. 从一只龙虾到14亿入口:ClawBot现象的本质是什么?最近,一个叫“微信ClawBot”的东西在圈子里火得不行。你可能没听过这个名字,但你大概率在微信群里见过它的“亲戚”——那些能陪你聊天、帮你查天气、甚至能写段子的机器人。Cla…

2026/10/13 18:45:05 阅读更多 →

最新新闻

TS流PCAP分析实战:从网络抓包定位音视频卡顿根源

TS流PCAP分析实战:从网络抓包定位音视频卡顿根源

1. 项目概述:为什么你手里的TS流PCAP文件比想象中更有价值“mpeg ts pcap 文件分析”——这串词看起来像一串技术黑话,但背后藏着大量真实业务场景里被忽视的关键信息。我接触过不少网络运维、音视频开发、IoT设备调试甚至安全分析一线的同行&#xff0c…

2026/10/14 6:05:23 阅读更多 →
C语言链表排序实战:插入排序与快速排序的指针操作详解

C语言链表排序实战:插入排序与快速排序的指针操作详解

简介:链表排序是C语言学习者绕不开的实践主题,这份源码压缩包面向正在掌握指针操作、动态内存分配和基础算法的初学者,帮助理解链表节点结构与排序算法的结合方式,也适合作为数据结构课程实验后的巩固练习。包体十分精简&#xff…

2026/10/14 6:05:23 阅读更多 →
从流量焦虑到AI获客:GEO优化师2026年实战复盘

从流量焦虑到AI获客:GEO优化师2026年实战复盘

1. 从流量焦虑到AI获客:一个优化师的2026年实战复盘过去这一年,我几乎每周都要跟不同行业的企业负责人聊同一个话题:钱花得越来越多,线索却越来越水。SEM账户里点击单价翻了一倍,信息流广告的转化率跌到千分之三以下&a…

2026/10/14 6:05:23 阅读更多 →
React Native鸿蒙跨平台固定表头长列表表格:双区布局与性能优化

React Native鸿蒙跨平台固定表头长列表表格:双区布局与性能优化

1. 一个被数据压垮的真实场景:长列表表格的"阅读灾难"上个月我接手了一个移动端报表类项目的数据展示模块,技术栈是 React Native,目标是跑通鸿蒙平台。先说结论:这个模块在改造之前,几乎处于"能看但没…

2026/10/14 6:05:23 阅读更多 →
Preliminary规格书阅读指南:锁参数、避坑、做版本跟踪

Preliminary规格书阅读指南:锁参数、避坑、做版本跟踪

简介:《PLA5506-940》是飞利浦光电提供的一份初步机密级产品规格书,版本V0.8,文档编号992215900750,适合光电与激光雷达、光通信、精密传感等领域的硬件工程师、选型评估人员和采购团队参考。该PDF围绕PLA5506-940型VCSEL阵列展开…

2026/10/14 6:05:23 阅读更多 →
一条富碱性多肽的进阶之路:序列解析、合成纯化与核定位验证

一条富碱性多肽的进阶之路:序列解析、合成纯化与核定位验证

1. 从一条氨基酸序列反推蛋白身份:这条肽的“户口本”是这样查的拿到 PDGF A-Chain (194-211) 这个编号,加上后面那一串 GRPREGSKKRKRKRLKPT,我的第一反应不是急着做实验,而是先把它当成一个“嫌疑人”来审:它到底来自…

2026/10/14 6:04:23 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →