C++的第一道门坎:类与对象(一)
1.面向过程与面向对象
1.1面向过程
我们之前学习的C语言就是一种面向过程的语言,面向过程的语言强调的是具体实现的过程,一般用函数来具体实现。我们用面向过程的思想,就可以把炒菜分为以下几个步骤:

1.2面向对象
而对于面向对象的语言而言,它将问题分为了多个对象,问题的解决由对象之间的交互完成,而不再注重对象完成事情的具体过程,譬如上述炒菜的过程我们即可抽象为人、菜、锅的交互。

C++即是一门面向对象的语言,我们更强调对象之间的交互,而并不在意对象该如何解决问题。
1.3对比
面向过程的语言和面向对象的语言各有其优缺点:
面向过程的语言流程化分工明确,效率高,但可维护性差,拓展能力差。
面向对象的语言结构化更清晰,易维护,但开销大,性能低。
2.类的引入
在C++中,我们在结构体的基础上引入了类(class)的概念,并对struct在CPP的基础上重新进行了解释。
由于我们的面向对象不注重过程的实现,但是对象本身还是需要实现过程的,因此我们要给对象加上一定的过程,比如我们的人锅交互可以炒菜,我们就要在类中声明一个作用是炒菜的函数。
也就是说,CPP的类和结构体中可以定义成员函数!
2.1类的声明方式
在C++中,有一个关键字为class,我们可以使用这个关键字来声明一个类,类内可以声明成员函数和成员变量,其语法结构为:
class 类名
{//类内的成员函数和成员变量
};
现在我们声明一个名为duanku的类:
class duanku
{int a;//成员变量int Add(int a, int b);//成员函数
};
2.2类的成员的两种定义方式
在定义类时,我们可以将类的定义与声明在同一文件书写,也可以在不同文件内书写。
2.2.1单文件定义
定义一个类,自然需要实现类体内的函数,我们可以将函数在类内定义,如下所示:
class duanku
{int a;//成员变量int Add(int a, int b)//成员函数{return a + b;}
};
这里需要大家注意的是:成员函数若在类内定义,则可能会被编译器当作内联函数处理。
2.2.2多文件定义
一个类也可以分为两个文件来实现,我们可以将成员函数的定义放在cpp文件内部。
但这儿需要大家注意的一点是:你定义的函数是谁的函数呢?是只在这个文件内部用的,还是实现了哪个地方的声明呢?因此,我们需要加域作用限定符,让编译器知道我们实现的是哪个函数。
//.h文件
class duanku
{int a;//成员变量int Add(int a, int b);//成员函数
};
//.cpp文件
int duanku::Add(int a, int b)
{return a + b;
}
PS:在日常使用时,我们可以将定义和声明都放在类中。但是在实际的工程中,更倾向于将声明和定义分离。
3.类的访问限定符与封装
3.1访问限定符
在C++类中有三种访问限定符:public、private、protect
它们的作用如下
- public修饰的成员可以在类外直接访问
- private和protect修饰的成员在类外不可直接被访问(后续文章会详细解释private和protect)
- 一个类中可以有多个访问限定符,一个限定访问符的作用域是该限定访问符出现的位置到下一个限定访问符出现的位置,如果后面没有限定访问符,则到类结束的位置为止。
- class的默认访问权限为private,struct的默认访问权限为public(下篇文章中会给出class和struct的详解)

3.2封装
封装:用类将对象的属性(数据)与操作数据的方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
通俗的说,封装其实是一种管理,是为了方便让用户更方便的使用类的一种方法。
比如说:对于一台电脑而言,我们提供给用户的是一系列的USB接口,我们可以通过它们和计算机进行一系列的交互,也可以自由更改每个USB接口的接什么,但是我们无法更改电脑的硬件元件。
这里,我们可以这样理解:
每个USB接口都是一个函数,我们可以自由更改它们,它们的访问限定为public。
而硬件元件则是类中的私有成员,不让外界访问,它们的访问限定为private。
4.类对象
4.1类对象的实例化
在类中定义的成员变量实际上是一种声明,而我们利用类名字定义的对象就是类对象的实例化。
即,用类的类型创造对象的过程,称为类的实例化。
1.类是对对象进行描述的,是一个模型一样的东西,限定了类中有哪些成员,定义出一个类并不会分配实际的内存空间来存储它。
2.一个类可以实例化出多个对象,实例化出的对象才占用实际的内存空间,存储的是类的成员变量。
我们不可以直接利用类来进行定义,如下图所示

正确的定义方法如下:
Date a;a._year = 3;
4.2类对象的存储
我们已经知道了类对象的创建方式,那么具体类中的成员变量与成员函数又是如何存储的呢?
4.2.1 存储方式1
每次创建对象时,都开辟一个空间存储类中的成员变量与成员函数。

但是这样存储就会出现一个问题,成员函数都是用的一个函数,我们每次都创建一个岂不是会有很大的开销吗?一个就能行的事儿为什么要用很多个呢?
于是,我们的计算机中,大多数都不是这么存储对象的。
4.2.2 存储方式2
我们首先将成员函数放在外面,之后我们每次创建对象时,都开辟一个空间存储类成员变量与成员函数的地址,需要成员函数的时候直接按照地址去找。

4.2.3存储方式3
每次创建对象时,都开辟一个空间存储类成员变量。而成员函数提前单独存储在另外一个区域(公共代码段)。
之后我们没去用到成员函数时,都去那个区域里面拿出来用。

一般的编译器采用的都是存储模式2或存储模式3,具体使用的是哪种存储模式,我们可以通过计算类大小来进行判断。
4.3类对象的大小
4.3.1普通类对象的大小
类型对象的大小也遵循结构体的内存对齐规则:
1.类的第一个成员对齐到结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的一个对齐数与该成员变量大小的较小值。
--VS2022中的默认对齐数是8
--Liunx中gcc没有默认对齐数,对齐数就是成员自身的大小
3.类总大小为所有成员对齐数中的最大对齐数的整数倍
4.如果嵌套了结构体,嵌套的结构体成员对齐到自己的成员的最大对齐数处,结构体的整体大小就是所有成员的最大对齐数(含嵌套结构体成员)的整数倍。
当然,类大小的计算我们可以也借助sizeof计算。
#include<iostream>
using namespace std;
class Date
{
//可以被直接访问
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}
//不能被直接访问
private:int _year;int _month;int _day;
};
int main()
{Date d;cout << sizeof(d) << endl;return 0;
}
由此可见,我们这里算出来的12是三个int类型的总和,而我们的成员函数则被放到了公共代码段中。 若是使用的存储模式2的话,则应该是12+成员函数地址的大小(32位系统是4,64位系统是8)。
4.3.2空类的计算
当类中只有成员函数,或者什么都没有时,类的大小是多少呢?
class A
{
public:void func(){;}
};
class B
{
};
int main()
{A a;B b;cout << sizeof(a)<<endl << sizeof(b);return 0;
}
这里我们发现空类的大小为1,现在我们可以思考这么一个问题,为什么空类的大小是1而不是0呢? 这是因为我们在进行空类的实例化时必须要有空间表示我们在这儿存储了一个对象。这时编译器就会默认给一个字节大小来标记这个对象。
5.this指针
5.1this指针的由来
#include<iostream>
using namespace std;
class Date
{
public://初始化void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date d1, d2;d1.Init(2022, 1, 11);d2.Init(2022, 1, 12);d1.Print();d2.Print();return 0;
}
在这里我先给大家抛出这么一个问题:为什么d1调用Init函数时,该函数知道是给d1对象调用Init,而不是给d2对象调用Init呢?
在C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数。通过不同的对象地址来分辨不同的对象,只不过所有的操作对用户是透明的的,即用户不需要来传递,编译器自动完成。实际代码如下:
void Init(Date* this, int year, int month, int day)
{this->_year = year;this->_month = month;this->_day = day;
}
d1.Init(&d1,2022, 1, 11);//实际传参
5.2this指针的特点
- this指针额类型为const,即我们无法给this指针赋值
- this指针的本质是“成员函数”的形参,当对象调用成员函数时,会将对象的地址当作实参传递给形参。
- this指针只能在成员函数中使用
- this指针是“成员函数”第一个隐藏起来的指针形参,一般由编译器放在通过eax寄存器自动传递,不需要用户传递。
5.3两个问题
5.3.1问题1
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class duanku
{
public:void Print(){cout << "Print()" << endl;}
private:int _a;
};
int main()
{duanku* p = nullptr;p->Print();(*p).Print();return 0;
}
这段代码是可以正常运行的,虽然p是一个空指针,但是由于我们的成员函数放在公共代码段中,因此我们还是可以找到这个函数的,不会因为对空指针解引用而找不到它。
5.3.2问题2
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class duanku
{
public:void Print(){cout << _a << endl;}
private:int _a;
};
int main()
{duanku* p = nullptr;p->Print();(*p).Print();return 0;
}
这里就会让程序崩溃,因为函数中访问了_a ,nullptr->_a程序就崩溃了。
6.成员变量的命名
大家在使用类时,可能会出现以下这么一个问题:
这时为了解决问题这类问题,我们在定义成员变量时会对其进行特定地修饰。比如说_变量名或者变量名_。不同公司,不同的程序员可能都有一套自己的命名规则,这里我们采用第一种。
由此我们可以将上述代码改写为这样:
class Date
{void Init(int year, int month, int day){_year = year;_month = month;_day = day;}int _year;int _month;int _day;
};
相关文章:
C++的第一道门坎:类与对象(一)
1.面向过程与面向对象 1.1面向过程 我们之前学习的C语言就是一种面向过程的语言,面向过程的语言强调的是具体实现的过程,一般用函数来具体实现。我们用面向过程的思想,就可以把炒菜分为以下几个步骤: 1.2面向对象 而对于面向对象的语言而言…...
经典面试题:MySQL如何调优?
目录 前言1. SQL查询优化2. 索引优化3. 表结构设计4. 硬件与配置优化5. 日常维护6. 性能测试与基准测试 前言 MySQL如何进行调优?这是面试中容易被问到的高频问题。 1. SQL查询优化 避免使用select* :只选取需要的列,减少数据传输量。使用…...
【程序员如何送外卖】
嘿,咱程序员要在美团送外卖,那还真有一番说道呢。 先说说优势哈,咱程序员那逻辑思维可不是盖的,规划送餐路线什么的,简直小菜一碟。就像敲代码找最优解一样,能迅速算出怎么送最省时间最有效率。而且咱平时…...
【git pull 和 push详解】
git pull 和 push详解 1.背景2.命令和解释2.1 git pull简介详情 2.2 git push简介Git Push 参数及详细解释 1.背景 在分布式开发环境中,git pull和git push的使用确保了团队成员之间的代码一致性,减少了不同步导致的问题。它们简化了版本管理,…...
数据挖掘导致直接路径读(direct path read)耗尽了IO
一大早就有喊业务卡的,检查等待事件源头,均为oracle写等待 查看IO负载持续维持在100%繁忙 后台有两个并行rman备份在,停止备份io繁忙没有好转,检查最近ash报告,发现DDTEK ODBC Oracle程序模块占用最高 检查该模块&…...
用队列实现栈 用栈实现队列 设计循环队列
用队列实现栈 思路 栈的特点:后进先出 队列的特点:先进先出 使用两个队列实现栈: 我们可以使用两个队列,一个队列为:空队列,一个队列为:非空队列 当我们要出队列时: 将 size - …...
BFS解决最短路问题(详解)
目录 BFS简介 && 框架: 一.二叉树的最小深度 二:迷宫中里入口最近的出口: 三.最小基因变化: 四:单词接龙: 五:为高尔夫比赛砍树: BFS简介 && 框架: 说到BFS…...
按尺寸筛选轮廓图中的轮廓
1.按短边筛选 原始轮廓图: import cv2 import numpy as np# 读取轮廓图 contour_image cv2.imread(..\\IMGS\\pp_edge.png, cv2.IMREAD_GRAYSCALE)# 使用cv2.findContours()函数获取所有轮廓 contours, _ cv2.findContours(contour_image, cv2.RETR_EXTERNAL, cv2…...
VBA高级应用30例:实现在列表框内及列表框间实现数据拖动
《VBA高级应用30例》(版权10178985),是我推出的第十套教程,教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开,这套教程案例与理论结合,紧贴“实战”,并做“战术总结”,以…...
「AIGC算法」R-tree算法
R-tree算法是一种非常实用的空间数据索引技术,它可以帮助我们在复杂的空间数据中快速找到我们想要的信息。下面我将用一些生活中的例子来帮助大家更好地理解R-tree算法。 1. 定义与原理 想象一下,你有一个巨大的图书馆,里面有成千上万本书,每本书都有它在书架上的特定位置…...
2024软考上半年嵌入式系统设计师考试回顾
一:考试准备工作 1:基本上都是提前30分钟进考场,进入考试教室的时候,会有监考老师核对身份证和准考证; 2:进入考试教室之后,会再一次核对身份信息,并且有监考老师手持扫描仪&#x…...
MIT6.828 Lab2-1 Using gdb
Using gdb gdb使用: xv6 gdb调试方法 问题1: Looking at the backtrace output, which function called syscall? 按照提示开启gdb后键入: b syscall c layout src backtrace输出结果: (gdb) backtrace #0 syscall () at k…...
mysqldump提示Using a password on the command line interface can be insecured的解决办法
mysql数据库备份一句话执行命令 mysqldump --all-databases -h127.0.0.1 -uroot -p123456 > allbackupfile.sql 提示如下提示 [rootyfvyy5b2on3knb8q opt]# mysqldump --all-databases -h127.0.0.1 > allbackupfile.sql mysqldump: Couldnt execute SELECT COLUMN_NA…...
Java毕业设计 基于springboot vue考勤管理系统
Java毕业设计 基于springboot vue考勤管理系统 SpringBoot 考勤管理系统 功能介绍 员工 登录 个人中心 修改密码 个人信息 员工请假管理 员工出差管理 薪资管理 员工签到管理 公告管理 管理员 登录 个人中心 修改密码 个人信息 员工管理 员工请假管理 员工出差管理 薪资管理…...
C数据结构:二叉树
目录 二叉树的数据结构 前序遍历 中序遍历 后序遍历 二叉树的创建 二叉树的销毁 二叉树的节点个数 二叉树叶子节点个数 二叉树第K层节点个数 二叉树的查找 层序遍历 判断二叉树是否为完全二叉树 完整代码 二叉树的数据结构 typedef char BTDataType; typedef str…...
使用Nginx作为反向代理实现MQTT内外网通信
使用Nginx作为反向代理实现MQTT内外网通信 步骤1: 安装Nginx 确保你的服务器上已安装Nginx。如果未安装,可以通过以下命令在Ubuntu上安装Nginx: sudo apt update sudo apt install nginx步骤2: 配置Nginx 编辑Nginx的配置文件,通常是/etc…...
SpringBoot 上传文件示例
示例效果: 前端代码: <html> <head><title>上传文件示例</title></head> <body> <h2>方式一:普通表单上传</h2> <form action"/admin/upload" method"post" enctyp…...
9.js函数
函数是js复杂数据类型的一种---可以理解为存放代码的盒子 用来帮助我们封装、复用、扩展以及调用代码的工具 函数的两个阶段 (1)声明函数(理解为创造) ——声明式声明 语法:function 函数名(参数){...代码} ——赋值时…...
关于数据库和数据表的基础SQL
目录 一. 数据库的基础SQL 1. 创建数据库 2. 查看当前有哪些数据库 3. 选中数据库 4. 删除数据库 5. 小结 二. 数据表的基础SQL 1. 创建数据表 2. 查看当前数据库中有哪些表 3. 查看指定表的详细情况(查看表的结构) 4. 删除表 5. 小结 一. 数据库的基础SQL 1. 创建…...
【C语言深度解剖】(14):结构体内存对齐(详细配图讲解)
🤡博客主页:醉竺 🥰本文专栏:《C语言深度解剖》 😻欢迎关注:感谢大家的点赞评论关注,祝您学有所成! ✨✨💜💛想要学习更多C语言深度解剖点击专栏链接查看&…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
