目录
- 引言
- 1.面向过程和面向对象初步认识
- 2.类的引入
- 3.类的定义
- 3.1声明与定义全部放在类体中
- 3.2声明与定义分离
- 4.类的访问限定符及封装
- 4.1访问限定符
- 4.2封装
- 5.类的作用域
- 6.类的实例化
- 类是对对象进行描述
- 一个类(一个类型变量)可以实例化出多个对象
- 7.类对象模型
- 7.1类对象的大小
- 7.2类对象的存储方式
- 7.3结构体内存对齐规则
- 8.`this` 指针
- 8.1 this指针的引出
- 8.2 this指针的特性
- 总结:
引言
在面向对象编程(OOP)中,类(Class)和**对象(Object)**是两个核心概念。
简单来说,**类定义了对象的模板,而对象是根据类创建的具体实体**
。例如,假设有一个类叫做Car,描述了汽车的属性(如颜色、品牌、型号)和行为(如加速、减速、转向)。那么根据这个类,我们可以创建多个具体的汽车对象,每个对象都有自己的属性和行为,但都基于相同的类模板。
在面向对象编程中,程序主要由对象组成,每个对象都是一个实例,具有特定的属性(成员变量)和行为(成员函数)。类则用来定义对象的模板,描述了一类对象共同的属性和行为。
1.面向过程和面向对象初步认识
面向对象编程(OOP)是一种以对象为中心的编程范式,它将数据和操作数据的方法封装在对象中,使得对象可以通过方法与其他对象进行交互。在面向对象编程中,程序的设计和实现是围绕着对象的概念展开的,强调对象之间的交互和继承关系,以及多态性的特性。
面向过程编程(POP)是一种以过程为中心的编程范式,它将问题分解为一系列的步骤或过程,然后按照顺序执行这些步骤来解决问题。在面向过程编程中,数据和操作数据的方法是分开的,程序的设计和实现是围绕着函数或过程的概念展开的,强调按照步骤执行任务。
C语言是面向过程的,关注的是过程,分析求解问题
而C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成
2.类的引入
相较于C语言,结构体中只能定义变量,而在C++中,结构体内不仅可以定义变量,还可以定义函数并在内部嵌套调用
如,C语言栈的实现
#include<iostream>using namespace std;
typedef int DataType;
struct Stack
{//初始化函数void Init(size_t capacity=5){_array = (int*)malloc(sizeof(int) * capacity);_capacity = capacity;_size = 0;}void Push(const DataType& data){_array[_size] = data;_size++;}DataType Top(){return _array[_size - 1];}void Destory(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}//成员变量DataType* _array;size_t _capacity;size_t _size;
};int main()
{Stack s;s.Init();s.Push(1);s.Push(3);s.Push(2);cout << s.Top() << endl;s.Destory();return 0;
}
而在C++中,结构体的定义,更习惯用 class
关键字来替代
3.类的定义
class name
{//成员函数//成员变量};
class 为定义类的关键字,name为类的名字
类的定义方式有俩种
3.1声明与定义全部放在类体中
若成员函数在类中定义,那么编译器可能会当作 内联函数来处理
3.2声明与定义分离
类声明放
4.类的访问限定符及封装
4.1访问限定符
C++实现封装的方式:类与对象的属性与方法结合在一起,设置访问权限的选择性将其接口提供外部使用
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- 如果后面没有访问限定符,作用域就到 } 即类结束。
- class的默认访问权限为private,struct为public(因为struct要兼容C)
4.2封装
在面向对象的三大特性中(封装、继承、多态),主要研究类的封装特性,在类和对象阶段
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来
和对象进行交互。
封装本质上是一种管理,让用户更方便使用类。
5.类的作用域
在C中,结构体的定义等价于一个变量的定义,而在C++中类的定义形成了一个新的作用域
C中有全局和局部,static静态
而C++中则形成了一个新的作用域,就好比之前,一块土地有公工区域(公共管理),和非公共区域(无人管理),然后类的定义,开始出现,私人住宅(别人允许你访问,那么便可以做客参观)
在类体外定义成员时,需要标识符 ::作用域操作符指明
void name::fun()
{//…………
}
6.类的实例化
用类类型创建对象的过程,称为类的实例化
类是对对象进行描述
定义一个类,并没有分配实际的内存空间来存储它
注:类当中的成员函数也没有空间? 类成员函数在定义后,就有了固定的存放位置,当创建类的实例化,并不会重复开辟成员函数的空间
一个类(一个类型变量)可以实例化出多个对象
多个类,分别占用实际的物理空间,储存类成员变量
类比,实例化出对象就像在现实中使用建筑设计图建造出房子,类就是设计图,房子就是实例化
7.类对象模型
7.1类对象的大小
类对象的大小取决于其成员变量和成员函数
1.成员变量的大小:类中的每个成员变量都占用内存空间。基本数据类型(如int、float等)通常占用固定大小的内存空间,而对象、指针或其他用户自定义类型的大小取决于其内部包含的成员变量。
2.对齐规则:在内存中,数据通常按照特定的对齐规则存储,以提高访问速度。对齐规则可以导致成员变量之间存在一些额外的空隙,以保证每个成员变量从正确的内存地址开始。对齐规则通常由编译器和目标平台决定。
3.继承和虚函数:如果类继承自其他类,或者包含虚函数,那么额外的内存空间可能会用于存储指向虚函数表的指针(对于包含虚函数的类)或基类的成员变量(对于派生类)。
如:
//前面所创建的Stack的类与Stack1比较
class Stack1
{int x;int y;int z;
};int main()
{cout << sizeof(Stack) << endl<<sizeof(Stack1)<<endl;
}
7.2类对象的存储方式
类对象包含类的各个成员
每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。
代码只保存一份,在对象中保存成员函数的地址
只保存成员变量,成员函数存放在公共的代码段
结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐
注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象
7.3结构体内存对齐规则
对齐规则和C语言中的结构体对齐规则是一样
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的对齐数为8
- 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
8.this
指针
8.1 this指针的引出
问题:当俩个同类型的类实例化,调用同一个成员函数,编译器是怎么分辨出,是哪一个面向对象调用函数?
如:
//引用之前所定义的Stack 的类
int main()
{Stack d1;Stack d2;d1.Init();d2.Init();//~~~~return 0;
}
这里调用Init初始化,怎么分辨出是d1,还是d2调用?
这就是this指针的作用了> 在C++中,this指针是一个特殊的指针,它指向当前对象的地址。它是每个非静态成员函数的隐式参数。当你在一个**成员函数内部引用类的成员变量或者调用其他成员函数**时,编译器会自动地插入this指针。
实现的底层逻辑:
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
8.2 this指针的特性
- this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
- 只能在“成员函数”的内部使用
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给
this形参。所以对象中不存储this指针。 - this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传
递,不需要用户传递
总结:
C++中通过类可以将数据 以及 操作数据的方法进行完美结合,通过访问权限可以控制那些方法在类外可以被调用,即封装,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。