【C++庖丁解牛】类与对象
📙 作者简介 :RO-BERRY
📗 学习方向:致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识
📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持
目录
- 1.面向过程和面向对象初步认识
- 2.类的引入
- 3.类的定义
- 4.类的访问限定符及封装
- 4.1 访问限定符
- 4.2 封装
- 5.类的作用域
- 6.类的实例化
- 7.类对象模型
- 7.1 如何计算类对象的大小
- 7.2 类对象的存储方式
- 7.3 结构体内存对齐规则
- 8.this指针
- 8.1 this指针的引出
- 8.2 this指针的特性
1.面向过程和面向对象初步认识
- C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
在C语言里对于洗衣服就会分成一个一个过程
- C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
2.类的引入
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:
之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数
C语言实现栈的代码:
typedef int DataType;
struct Stack
{void Init(size_t capacity){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (nullptr == _array){perror("malloc申请空间失败");return;}_capacity = capacity;_size = 0;}void Push(const DataType& data){// 扩容_array[_size] = data;++_size;}DataType Top(){return _array[_size - 1];}void Destroy(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}DataType* _array;size_t _capacity;size_t _size;
};
int main()
{Stack s;s.Init(10);s.Push(1);s.Push(2);s.Push(3);cout << s.Top() << endl;s.Destroy();return 0;
}
C++里面兼容C语言struct的所有用法
但是在C++中更喜欢用class来代替,这叫做类
3.类的定义
class className
{// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
类的两种定义方式:
- 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
一般情况下,更期望采用第二种方式。
成员变量命名规则的建议:
我们看看这个函数,是不是很僵硬?
class Date
{
public:void Init(int year){// 这里的year到底是成员变量,还是函数形参?year = year;}
private:int year;
};
所以一般都建议这样
class Date
{
public:void Init(int year){_year = year;}
private:int _year;
};
或者这样
class Date
{
public:void Init(int year){mYear = year;}
private:int mYear;
};
其他方式也可以的,主要看公司要求。一般都是加个前缀或者后缀标识区分就行
4.类的访问限定符及封装
4.1 访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选
择性的将其接口提供给外部的用户使用
我们先来看一看类的工作方式:
#include<iostream>
using namespace std;
class Date
{
private:int _day;int _month;int _year;
public:void Init(int day,int month,int year){_day=day;_month=month;_year=year; }
}
关键字private和 public是新的,它们描述了对类成员的访问控制。使用类对象的程序都可以直接访问公有部分,但只能通过公有成员函数(或友元函数)来访问对象的私有成员。
例如:要修改 Date 类的 day 成员,只能通过 Date 的成员函数。因此,公有成员函数是程序和对象的私有成员之间的桥梁,提供了对象和程序之间的接口。防止程序直接访问数据被称为数据隐藏(C++还提供了第三个访问控制关键字protected,介绍类继承时将讨论该关键字。)
类设计尽可能将公有接口与实现细节分开。公有接口表示设计的抽象组件。将实现细节放在一起并将它们与抽象分开被称为封装。数据隐藏(将数据放在类的私有部分中)是一种封装,将实现的细节隐藏在私有部分中。封装的另一个例子是,将类函数定义和类声明放在不同的文件中。下面我们会具体介绍封装
【访问限定符说明】
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- 如果后面没有访问限定符,作用域就到 } 即类结束。
- class的默认访问权限为private,struct为public(因为struct要兼容C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
【面试题】
C++中struct和class的区别是什么?
C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来
定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类
默认访问权限是private。注意:在继承和模板参数列表位置,struct和class也有区别,后序给大
家介绍。
4.2 封装
【面试题】
面向对象的三大特性:封装、继承、多态。
在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
**封装本质上是一种管理,让用户更方便使用类。**比如:对于电脑这样一个复杂的设备,提供给用户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。
对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来
隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。
5.类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 ::
作用域操作符指明成员属于哪个类域。
在类中定义的名称(如类数据成员名和类成员函数名)的作用域都为整个类,作用域为整个类的名称只在改类中是已知的,在类外时不可知的。因此,可以在不同类中使用相同的类成员名而不会引起冲突。另外,类作用域意味着不能从外部直接访问到类的成员,共有成员函数也是如此,也就是说,要调用公有成员函数,必须通过对象:
Date A; //创建类
Date.Init(2,4,2024); //调用类内部公有函数
Init(2,4,2024); //错误用法,查无此函数
同样在类外定义成员函数时,必须使用作用域解析运算符:
void Date::show() //两个连续冒号即是域解析运算符,前面跟上类的名称即可
{...
}
使用类成员(类成员变量、类成员函数)时,必须根据上下文使用直接成员运算符
(.)
、间接成员运算符(->)
或作用域解析运算符(::)
。
6.类的实例化
用类类型创建对象的过程,称为类的实例化
- 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个类,来描述具体学生信息。类就像谜语一样,对谜底来进行描述,谜底就是谜语的一个实例。谜语:“年纪不大,胡子一把,主人来了,就喊妈妈” 谜底:山羊
- 一个类可以实例化出多个对象,实例化出的对象会占用实际的物理空间,存储类成员变量
int main()
{Person._age = 100; // 编译失败:error C2059: 语法错误:“.”return 0;
}
Person类是没有空间的,只有Person类实例化出的对象才有具体的年龄
int main()
{Person A; //实例化对象A._age = 100; //运行成功return 0;
}
- 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设
计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象
才能实际存储数据,占用物理空间
7.类对象模型
7.1 如何计算类对象的大小
类的内存大小是多少呢?
我们前面学习过结构体的内存对齐规则,但是那是c语言
而C++又基本包含C语言的知识
那么C++类的大小是多少呢?
在这里a的大小是多少呢?
Init成员函数指针在不在内存大小里面呢?
结论是成员函数不在里面,类的大小只考虑成员对象的大小空间,成员函数不算成员对象。
那么成员函数为什么不在成员对象里面呢?
我们想调用函数是需要调用地址的
这里两个不同类对象的Init函数所调用的地址是不是一样的呢?
答案是一样的
7.2 类对象的存储方式
存储方式如下图:
类的成员对象是相互独立的,但是对于一个类的成员函数它是放在一个公共区域的,就相当于一个小区,有很多户人家,也就是实例化了很多个对象,但是在这个小区里篮球场,广场只有一个,也就是公共区域,每家每户都可以随意使用。
在这里也就是我们可以每个实例化对象都可以访问这个成员函数,但是这个成员函数是只初始化一次的,也就是放在公共区域,所以每一个类的实例化对象所计算内存大小只会计算成员对象,不会去计算成员函数,成员对象的内存计算就是该类中”成员变量”之和,当然要注意内存对齐
- 但是这对于空类却不一样
运行结果:
无成员变量的类,对象大小开一个字节,这个字节不存储有效数据
标识定义的对象存在过
结论:
一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐
注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。
7.3 结构体内存对齐规则
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8- 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
8.this指针
8.1 this指针的引出
我们先来定义一个日期类 Date
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;
}
运行结果:
对于上述类,有这样的一个问题:
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函
数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏
的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”
的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编
译器自动完成。
所以,系统会将如上代码改成下面的这样:其中有函数参数Date *this
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(Date *this){cout << this->_year << "-" <<this->_month << "-" << this->_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(&d1);d2.Print(&d2);return 0;
}
注意:
我们是不能显示的写this参数的
8.2 this指针的特性
- this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
- 只能在“成员函数”的内部使用
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
【面试题】
- this指针存在哪里?
this指针是形参,是存在栈帧上面的,会被系统回收销毁的,在vs里面this存在寄存器里
相关文章:

【C++庖丁解牛】类与对象
📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 目录 1.面向过程和面向对象…...

在什么时候企业档案才会发生调整
档案在企业中通常会调整在以下几个时刻: 1. 入职时:员工入职时,企业会要求员工提供个人档案,包括身份证件、学历证明、工作经历等相关文件。 2. 离职时:员工离职时,企业会整理员工的离职档案,包…...
Linux或Windows下判断socket连接状态
前言 场景:客户端程序需要实时知道和服务器的连接状态。比较通用的做法应用层是采用心跳机制,每隔一端时间发送心跳能回复说明服务器正常。 实际应用场景中,服务端和客户端并不是一家厂商的,比如说笔者这种情况,服务端…...
编译链接实战(25)gcc ASAN、MSAN检测内存越界、泄露、使用未初始化内存等内存相关错误
文章目录 1 ASAN1.1 介绍1.2 原理编译时插桩模块运行时库2 检测示例2.1 内存越界2.2 内存泄露内存泄露检测原理作用域外访问2.3 使用已经释放的内存2.4 将漏洞信息输出文件3 MSAN1 ASAN 1.1 介绍 -fsanitize=address是一个编译器选项,用于启用AddressSanitizer(地址...

[HackMyVM]靶场 VivifyTech
kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 0a:00:27:00:00:05 (Unk…...
软考高级系统分析师:关联关系、依赖关系、实现关系和泛化关系概念和例题
一、AI 解读 关联关系、依赖关系、实现关系和泛化关系是面向对象设计中的四种基本关系。它们在类与类之间建立不同类型的联系,以反映对象间的相互作用、依赖和继承关系。下面我将使用表格的形式来解释这四种关系的概念和它们之间的区别: 关系类型概念特…...
设计模式学习笔记 - 面向对象 - 9.实践:如何进行面向对象分析、设计与编码
1.如何对接口鉴权这样一个功能开发做面向对象分析 本章会结合一个真实的案例,从基础的需求分析、职责划分、类的定义、交互、组装运行讲起,将最基础的面向对象分析(00A)、设计(00D)、编程(00P&…...

【iOS ARKit】RealityKit 同步机制
协作 Session 可以很方便地实现多用户之间的AR体验实时共享,但开发者需要自行负责并确保AR场景的完整性,自行负责虚拟物体的创建与销毁。为简化同步操作,RealityKit 内建了同步机制,RealityKit 同步机制基于 Multipeer Connectivi…...

【数据结构与算法】整数二分
问题描述 对一个排好序的数组,要求找到大于等于7的最小位置和小于等于7的最大位置 大于等于7的最小位置 易知从某个点开始到最右边的边界都满足条件,我们要找到这个区域的最左边的点。 开始二分! left指针指向最左边界,right…...

java项目打包运行报异常:xxxxx-1.0-SNAPSHOT.jar中没有主清单属性
pom.xml中加入这段话即可 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.4.4</version><executions><execution><…...
MAC-键盘command快捷键、设置windows快捷键
在 Windows PC 专用键盘上,请用 Alt 键代替 Option 键,用 Ctrl 键或 Windows 标志键代替 Command 键。 Mac 键盘快捷键 - 官方 Apple 支持 (中国) 设置windows快捷键 使用mac外接适用于windows的键盘时,如何设置快捷键?_mac外…...

C++ 补充之常用遍历算法
C遍历算法和原理 C标准库提供了丰富的遍历算法,涵盖了各种不同的功能。以下是一些常见的C遍历算法以及它们的概念和原理的简要讲解: for_each:对容器中的每个元素应用指定的函数。 概念:对于给定的容器和一个可调用对象ÿ…...

【Linux杂货铺】调试工具gdb的使用
目录 🌈前言🌈 📁背景介绍 📁 使用 list [行号] / [函数名] run/r break/b [行号] / [函数名] info break disable break enable break delete break [断点编号] next/n step/s continue/c finish print/p [变量…...

FL Studio Producer Edition2024中文进阶版Win/Mac
FL Studio Producer Edition,特别是其【中文进阶版 Win/Mac】,是数字音乐制作领域中的一款知名软件。它为广大音乐制作人、声音工程师以及音乐爱好者提供了一个从音乐构思到最终作品发布的完整解决方案。这个版本特别为中文用户优化,并兼容W…...

无需邀请码,Xinstall实现精准分享归因
在如今的移动互联网时代,分享已经成为了我们日常生活中不可或缺的一部分。无论是社交媒体上的好友分享,还是应用内的内容分享,分享都能够帮助我们快速传播信息,扩大影响力。然而,对于开发者而言,分享却带来…...

机器人与AGI会撞出什么火花?
真正的科技变革是不是就要来临了?各方大佬都开始布局机器人,对于普通人的就业会造成什么影响? 优牛企讯-企业动态信息监控专家 在优牛企讯-企业动态监控专家搜索可知,全国目前的机器人公司已经达到了26401家,近一年…...

Linux yum安装pgsql出现Bad GPG signature错误
官方文档:https://www.postgresql.org/download/linux/redhat/ sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo yum install -y postgresql12-server sudo /usr/pgsql-12/bin/…...

第18章-DHCP
1. 产生背景 2. 概述 2.1 定义 2.2 特点 2.3 DHCP系统组成 3. DHCP工作原理 3.1 前提条件 3.2 场景 3.3 分配IP地址工作机制 3.4 特殊情况处理 3.5 IP地址租约更新 4. DHCP中继代理 4.1 现实场景 4.2 工作机制 1. 产生背景 现实问题: 小型网络中&…...

[物联网] OneNet 多协议TCP透传
[物联网] OneNet 多协议TCP透传 STM32物联网–ONENET云平台的多协议接入产品创建 : https://blog.csdn.net/qq_44942724/article/details/134492924 Onenet tcp 透传 : https://blog.csdn.net/flyme2010/article/details/107086001 tcp服务端测试工具 : http://tcp.xnkiot.com/…...

如何让网页APP化 渐进式Web应用(PWA)
前言 大家上网应该发现有的网页说可以安装对应应用,结果这个应用好像就是个web,不像是应用,因为这里采用了PWA相关技术。 PWA,全称为渐进式Web应用(Progressive Web Apps),是一种可以提供类似…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...

yaml读取写入常见错误 (‘cannot represent an object‘, 117)
错误一:yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因,后面把yaml.safe_dump直接替换成yaml.dump,确实能保存,但出现乱码: 放弃yaml.dump,又切…...