文章目录
- 前言
- 浅浅了解
- 一、面向过程和面向对象
- 二、 类和对象的关系
- 三、创建类和对象
- 逐步深入
- 一、类的访问限定符
- 二、 封装
- 三、类的作用域
- 四、类对象模型
- 五、this指针
前言
浅浅了解
一、面向过程和面向对象
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题.
比如洗衣服这件事:
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
二、 类和对象的关系
上面洗衣服的模型,有4个对象:人、衣服、洗衣粉、洗衣机。
我们对于这些对象是如何定义的呢?是通过类来定义的。
类是对象的模板或蓝图,对象则是类的实例化,即根据类创建出来的具体实体。
简单说:类是房子设计图,对象是房子
三、创建类和对象
那我们如何定义一个类呢?如下
具体如下:
在类中,我们不但可以定义成员变量即上面的a,top,capacity,可以定义成员函数即上面的Init(),push()。
其中成员函数可以定义在类的外面,声明在类的里面。
创建一个对象并使用成员函数
其中stack是一个类,st是stack的一个实例对象。
逐步深入
看完上面如何创建一个类、对象,你肯定有很多疑惑。为什么类会有成员函数?类为什么会有public,private? 类外定义成员函数为什么要使用作用域解析运算符::
?下面我们开始渐渐深入了解。
一、类的访问限定符
以前在C语言中,我们使用结构体来自定义类型,但使用结构体有一些弊端。假如我们现在定义一个数据结构 - 栈。
同理,StackPush(),StackPop()……都可以不用写函数,直接访问结构体成员就可以了。即使数据结构要求使用函数,但你并没有禁止直接访问结构体成员。于是c++之父对结构体的访问做出一些规定,即添加了访问限定符
在c++中,结构体被升级为了类,不过我们一般不用结构体来定义类,而是用一个新的关键字class来替代struct。
c++的访问限定符有以下三种:
public - 表示可以在外界访问。
private - 表示只能在类里访问。
protected, - 表示只能在类和该类的派生类里访问。
在两个访问限定符之间的内容,它的访问权限为第一个访问限定符。
如下:
这时,你就只能通过类的成员函数来改变了。
二、 封装
面向对象的三大特性:封装、继承、多态。
在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
封装本质上是一种管理,让用户更方便使用类。比如:对于电脑这样一个复杂的设备,提供给用户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑
真正工作的却是CPU、显卡、内存等一些硬件元件。
类中添加成员函数,目的是为了将相关的数据和操作封装在一起,使得数据和操作成为一个整体,从而更好地组织代码和实现功能。成员函数可以访问和修改类的成员变量,也可以进行复杂的计算和逻辑操作,对外部提供更为友好的接口。
同时,使用成员函数还能够实现面向对象编程的一些基本特性,例如封装、继承、多态等,更好地体现了面向对象编程的思想。另外,成员函数也可以作为类之间进行交互和通信的接口,方便实现各个类之间的协同工作。
三、类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用
::
作用域操作符指明成员属于哪个类域。
当你在外界定义成员函数时,需要使用::
来指明函数属于哪一个类域。
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
cout << _name << " "<< _gender << " " << _age << endl;
}
四、类对象模型
对于一个类的,我们该如何计算它的大小呢?
这和计算结构体的大小一致。
结构体对齐规则:
结构体(struct)的数据成员, 第一个数据成员存放的地址为结构体变量偏移量为0的地址处.
其他结构体成员自身对齐时, 存放的地址为min{ 有效对齐值为自身对齐值, 指定对齐值 } 的最小整数倍的地址处.
注 : 自身对齐值 : 结构体变量里每个成员的自身大小
注 : 指定对齐值:有宏 #pragma pack(N)指定的值, 这里面的 N一定是2的幂次方.如1, 2, 4, 8, 16等.
如果没有通过宏
在32位Linux主机上默认指定对齐值为4, 64位的默认对齐值为8,
AMR CPU默认指定对齐值为8;
vs-8
注:有效对齐值:结构体成员自身对齐时有效对齐值为自身对齐值与指定对齐值中 较小的一个.
总体对齐时, 字节大小是min{ 所有成员中自身对齐值最大的, 指定对齐值 } 的整数倍.
类的大小的计算方式也是这样,但是类新增了成员函数,成员函数的大小难道不计算吗?这就要先了解一下类的存储方式了。
请问类采用哪种存储方式?
答案是第三种,成员函数不放在对象中,而是放在公共代码段。
我们只用测试一下类的大小就知道了。
可见,成员函数并没有计算在其中,至于为什么,其实想想也很知道,如果每个对象都自带函数,这就太浪费,不如每一个对象都共用同一个函数。
对于一个空类,它的大小规定为1字节。
在C++中,空类的大小虽然没有定义成员变量,但是由于每一个实例都应该拥有一个唯一的地址,因此编译器为其分配了一个字节的空间以确保该类的每一个对象都具有唯一的地址。
空类继承其他类或者包含虚函数时,所占用的空间将取决于继承或虚函数表的大小,而不是定为1字节。
五、this指针
对于成员函数,你是否有下面的疑惑?
答案:很显然不是,成员函数里的a是实例对象里的a,类里面的成员变量只是“图纸”。
那这里就有一个问题了,成员函数里的成员变量是实例对象里的成员变量,上面又说,成员函数是放在公共代码段,所有对象共用,那成员函数是怎么区分?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
编译器进行了暗箱操作,也就是说,你设置的成员函数都自带一个隐形参数,只不过你看不到,而且不允许你手动加上。
this指针的特性
-
this指针的类型:类的类型* const,即成员函数中,不能给this指针赋值。
-
只能在“成员函数”的内部使用,不允许你添加在函数实参/形参里
-
this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。
所以对象中不存储this指针。 -
this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用
户传递
根据this指针的特性,回到以下两个问题:
- this指针存在哪里?
- this指针可以为空吗?
答案:this指针存储在栈上,因为它是形参。
this指针可以为空,但要满足一定条件