面向对象和面向过程的认识
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
而C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
那什么是过程?什么是对象呢?
我们在生活中举例子:比如我要去烧一壶开水
对象:我,热水壶,水,排插/插座,热水壶托盘
过程:把水接到水壶里 --> 关闭水壶盖 --> 把热水壶放到托盘中 --> 连接好插排/插座 --> 按下热水壶开关 --> 静等几分钟 -->一壶水烧开了
类
类的引入(以下以Stack为例)
在C语言中,我们学过结构体,但在C语言的结构体当中,只能由成员变量组成,回顾一下怎么用C语言实现(Stack)栈的呢?
然而在C++当中,结构体里不仅可以定义成员变量,还可以定义成员函数,那就是“类”
类的介绍
在C++当中,喜欢把struct写成class(类),什么是类?
class ClassName { //由 成员变量 和 成员函数 来组成 };
注:1、class和struct相似, 2、ClassName是类的名称,
3、名称后需跟中括号{},4、右中括号后还有分号 ( ; )
那么在C++当中关于栈的类是怎么定义的呢?
类的第一种定义方法(用这种方便)
在class(类)中声明和定义成员变量和成员函数(可以在class中具体实现成员函数)
注:(成员函数如果放在类体中,编译器可能会当成内联函数处理)
(关于访问权限我们在后续中会讲到,这里先做铺垫)
类的第二种定义方法:类的作用域(实际期望用这种)
类定义了一个新的作用域,类的所有成员都在类的作用域中。
在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
在class(类)中定义成员变量和定义成员函数,
在.cpp文件中声明成员函数 (也就是通过类和作用域限定符指定实现Stack类中的某个函数)
成员变量命名规则
建议①
成员变量用 _year 表示
建议②
成员变量用 前缀(m_year) 或者 后缀(例如:year_m)来表示
类的访问限定符
访问限定符的介绍
C++实现封装(文章下面会讲到)的方式:
用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
访问限定符包括:public(公有),protected(保护),private(私有)
注: 1、public修饰的成员在类外是直接可以访问的
2、private修饰的成员在类外不能直接被访问(protect和private是类似的)
3、访问权限作用域从访问权限开始到下一个访问权限出现结束
4、 如访问没有限定符,那就到 } 结束
5、class默认访问权限是private,struct默认访问权限是public(struct兼容C语言)
封装
面向对象的三大特性:封装、继承、多态。
在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来 和对象进行交互。
封装本质上是一种管理,让用户更方便使用类
就像上面说的 面向对象和面向过程的认识 一样
烧一壶开水,我们只需关心把水装进水壶,再按下按钮就可以了
并不关心开水是怎么烧开的,它的内部用什么装置和线路组成的(封装)
用户只需要与厂家提供的按钮,热水壶等显而易见直接实现的外部装置之间进行交互
在C++语言中实现封装,
可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。
类的实例化
概念
用类类型创建对象的过程,称为类的实例化
类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,
定义出一个类并没有分配实际的内存空间来存储它
打个比方:
类实例化出对象就像现实中使用建筑设计图建造出房子,
类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,
同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间
类 和 对象 是 一对多的关系
一个类可以有多个对象,比方类是设计图,对象是通过设计建出来的房子,
一个设计图可以设计建造出多个类似的房子
理解类的实例化(含图片和代码)
举例:Data日期类
class Data { public: void Init(int yaer, int month, int day) { m_year = yaer; m_month = month; m_day = day; } private: int m_year; //年 int m_month;//月 int m_day; //日 }; int main() { Data s1; //定义(实例化)对象 Data s2; //定义(实例化)对象 s1.Init(2024, 4, 25); s1.Init(2024, 4, 25); Data s3; //定义(实例化)对象 Data s4; //定义(实例化)对象 s1._yaer++; //成员变量的权限为private s2._yaer++; return 0; }
类对象模型
计算类对象大小
(看下面Data类,类A、B、C)
class Data { public: void Init(int yaer, int month, int day) { _year = yaer; _month = month; _day = day; } private: int _year; int _month; int _day; }; class A {}; class B { private: int a; char b; }; class C { public: void D(){} }; int main() { Data d1; cout << sizeof(d1) << endl; cout << sizeof(A) << endl; cout << sizeof(B) << endl; cout << sizeof(C) << endl; }
1.怎么计算类对象的大小呢?(Data类,类A、B、C)它们的大小分别为多少?
一个类的大小,实际就是该类中”成员变量”之和,
①为什么不算成员函数?
实例化的不同对象调用函数是同一个函数指针,同一个地址,每个对象都放一份,会大大浪费
2.空类类对象大小为1
当然要注意内存对齐注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。
控制台输出显示
结构体内存对齐
详细讲解请看该篇文章:
this指针
this指针的引入
看一下前面提到的Data(日期类)
class Data { public: void Init(int yaer, int month, int day) { _year = yaer; _month = month; _day = day; } void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; int main() { Data d1; Data d2; d1.Init(2024, 4, 27); d2.Init(2023, 4, 27); d1.Print(); d2.Print(); return 0; }
现有这样一个问题,
当d1调用Init成员函数时,该函数如何知道应该设置d1对象而不是设置d2对象呢?
C++中通过引入this指针解决该问题,
即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),
在函数体中所有“成员变量”的操作,都是通过该指针去访问。
只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针的特性
1、this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
2、 只能在“成员函数”的内部使用
3、this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
4、this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传
递,不需要用户传递
d1和d2都调用该成员函数,但结果却不同的原因是:
void Print() 函数再编译器编译是 void Print(Data* const this),有一个隐藏参数this指针
d1调用函数时,是这样子来访问的:
&d1->_year、&d1->_month、&d1->_day,
此时this指针就是d1的指针,&d1是实参,Data* this是形参(局部变量),访问的是d1的对象_year,_monh,_day
C语言和C++实现Stack的对比
C语言
共性
1、每个函数的第一个参数都是Stack*
2、函数中必须要对第一个参数检测,因为该参数可能会为NULL函数中都是通过Stack*参数操作栈的
3、调用时必须传递Stack结构体变量的地址
结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据的方式是分离开的,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出错。
C++
C++中通过类可以将数据 以及 操作数据的方法进行完美结合,
通过访问权限可以控制那些方法在类外可以被调用,即封装,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。
而且每个方法不需要传递Stack*的参数了,编译器编译之后该参数会自动还原,
即C++中 Stack * 参数是编译器维护的,C语言中需用用户自己维护。