类和对象入门级:第六篇
- 1.类的引入
- 2.类的定义
- 2.1类的访问限定符
- 2.2类的封装
- 2.3类的实例化
- 3.如何计算类或者对象的大小
- 4.this指针
- 总结
我们知道,C++在C语言的基础上引入了对象的概念,那么从本篇开始进入类和对象;
1.类的引入
🤔首先,什么是类呢?
类(Class)具有共同属性和行为的事物所构成的集合,是面向对象程序设计实现信息封装的基础。类与C语言中的结构相似,是既能表示属性又能表示行为的复合数据类型。
小哈士奇类:
它们都有狗狗的属性,眼睛都很蓝的属性,很可爱的属性等等;
总而言之,类是对某一类具有共同特征的事物的抽象描述;
👉我们接触的第一个类👈
概念:
C语言结构体中只能定义变量,然而在C++中,我们把结构体升级成了类,结构体内不仅可以定义变量,也可以定义函数,这个变量叫做成员变量,这个函数叫做成员函数,结构体名可以做类型名(可以省略struct了);
- 结构体名可以做类型名
- struct内可以写函数
3. 调用成员函数和成员变量
2.类的定义
我们在C++中我们通常喜欢用class(类的意思),来定义类;
类的写法:
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略
🤔为什么换成class就报错了?
这就涉及到了类的访问限定符了;
2.1类的访问限定符
注解:
- public:类外面可以访问;
- protected和private(暂时认为是一样的):类外的不能直接访问类里的;
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止,如果后面没有访问限定符,作用域就到 } 即类结束;
- . class的默认访问权限为private,struct为public(因为struct要兼容C);
🤔🤔那我们如何才能访问class中的函数呢?
🤔🤔🤔如果我们写的一个函数很长,我们一般声明和定义分开写,在类中同样如此!
正确定义:
所以一般短的函数我们直接定义在类中,类会将这个函数默认为内联函数(因为声明和定义不能分离),一般长的函数我们声明和定义分离;
🤔🤔🤔我们注重一下成员变量的写法,否则可读性就会变差,比如这里的year到底是成员变量,还是函数形参?
class Date
{
public:
void Init(int year)
{
// 这里的year到底是成员变量,还是函数形参?
year = year;
}
private:
int year;
};
处理方法(按自己的来就行):
2.2类的封装
面都对象的三大特征:封装,继承,多态;
🤔那么什么是封装呢?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互;
就像电脑一样:
🤔🤔为什么要封装?
- 可以保护对象的实现细节,防止外界对其进行不当操作;
- 可以方便地检查和保证对象被正确地使用,避免出现访问越界等错误;
- 可以提高程序的可读性和可维护性;
总而言之,封装的本质是为了更好的管理,更安全的使用类;
2.3类的实例化
🤔🤔🤔什么是类的实例化?
用类类型创建对象的过程,称为类的实例化;
有同学可能会有这样的疑问:我们能不能从类域中访问成员变量?
注意这里的类只是声明,并没有实例化对象,只有实例化对象了我们才能通过对象进行访问
3.如何计算类或者对象的大小
🤔🤔🤔问题来了,一个类实例化的对象的大小是多少字节?
结论: 类的大小的计算遵循内存对齐的规则,只计算成员变量的大小;
👇如果大家忘了内存对齐规则可以看看这篇博客
内存对齐
内存对齐规则:
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意: 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值,VS中默认的对齐数为8; - 结构体总大小为:最大对齐数的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
🤔🤔🤔为什么要内存对齐?
我们从一个简单的代码分析内存对齐与不进行内存对齐:
struct tap
{
char a;
int b;
};
注意:
A.从内存中一次读取多少字节是由(数据总线)硬件控制的,那么这个大小是固定的,不同的的平台读取多少字节是不一样的,我们以每次从内存中读取4Byte为例;
B.我们以一次从内存中读取多少字节(这是由硬件控制我们以4byte为例) 来划分若干个内存边界,我们只能从内存边界开始读取数据,不能随意的读取(因为硬件不支持);
- 对于不内存对齐:
- 对于内存对齐:
总结:
1.平台原因:
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能 在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因 :
数据结构( 尤其是栈 ) 应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。总的来说就是以空间换时间 。
注意: 修改默认对齐数只是改变了数据的存储方式,不会改变一次从内存中读取多少字节的数据(因为硬件是固定的);
🤔🤔🤔为什么不计算成员函数的大小?
因为,每个对象的成员变量是不同的,但是所调用的函数是一致的,所以没必要每个对象都存储成员函数;
所以,对象中只存储成员变量,不存储成员函数;
🤔🤔🤔那为什么在C++的类中没有成员变量却还要占一个字节呢?
语法规定: 没有成员变量的类对象,需要占用 1 Byte,是为了占位,表示对象存在,不存储有效数据;
🤔🤔🤔🤔那这些成员函数都存在哪?
既然成员函数是类实例化的对象所共有的,那我们肯定不能存放在对象中 (太浪费空间没必要),所以我们将这些成员函数存放在公共代码区;
公共代码区:
- 存放函数体的二进制代码,由操作系统进行管理。
- 代码区是共享的,对于被频繁执行的程序,只需要在内存中有一份代码即可。
- 代码区是只读的,防止意外修改代码。
这样存储更能节省空间,这些成员函数会在编译阶段直接调用(call 函数地址),建立函数栈帧;
4.this指针
👉由问题引入this指针👈
🤔🤔🤔为什么d1,d2调用的是同一个函数但是打印的结果不同?
这个就涉及到了隐含的this指针,这个是由编译器来完成的;
本质上调用的是同一个函数,只是编译器处理的参数不一样,这是编译器自动完成的,不需要我们操作;
我们发现一个报错
语法规定:
- this指针不能在实参和形参之间显示传递,但是可以在函数内部使用this指针;
- this指针是不可修改的;
🤔🤔🤔this指针是存在内存的哪个区?
栈区 ,堆区 ,静态区 ,常量区 ,公共代码区,中的哪个区域呢?
先说答案this是存在内存的栈区的,vs对this指针进行了优化,存放在ecx(寄存器)中,这样传参更快
分析:首先this指针是一个形参,所以this指针和普通形参一样是存放在栈里面的,作为栈帧的一部分;
😁好了我们学完了this指针,就拿两道题试试手(dog
分析:
总结
以上就是本篇的所有内容了,今天主要学了类和对象的入门篇,什么是类,类的定义,类的实例化,如何计算类的大小,以及this指针及一些注意点,如果喜欢本篇,不妨点个赞❤️,下篇见;