1.类和对象的初步认识
1.1面向过程和面向对象的区别
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象, 靠对象之间的交互完成。
举一个简单的例子:
设计简单外卖系统
面向过程:关注实现下单、接单、送餐这些过程。体现到代码层面--方法/函数
面向对象:关注实现类对象及类对象间的关系,用户、商家、骑手以及他们之间的关系。体现到代码层面 --类的设计及类之间关系
1.2类的引入
在C语言中,我们已经学过了结构体这个自定义类型,我们就用结构体来定义一个学生的类型吧:
struct Student { char name[10]; int age; int id; }; int main() { struct Student s1; //兼容C Student s2; //升级到类,Student为类名,也是类型 return 0; }
由于C++兼容C的语法,所以struct在C++中也升级成了类,在C语言中,我们在定义结构体变量后,不能对成员变量进行初始化,但是,在C++中,我们可以在结构体中定义成员函数,同时可以利用成员函数对结构体变量进行初始化等操作,来看看是怎样操作的吧:
struct Student { //成员变量 char _name[10]; int _age; int _id;//前面加_是为了和后面函数的形参进行区分 //成员方法 //初始化成员变量 void Init(const char* name, int age, int id) { strcpy(_name, name); _age = age; _id = id; } //打印成员变量 void Print() { cout << _name << endl; cout << _age << endl; cout << _id << endl; } }; int main() { struct Student s1; //兼容C Student s2; //升级到类,Student为类名,也是类型 s1.Init("张三", 18, 1); s2.Init("李四", 19, 2); s1.Print(); s2.Print(); return 0; }
打印输出的结果:
1.3内的定义
class为定义类的关键字:
class className { //类体:由成员变量和成员函数组成 };
那么,上面那段结构体代码的关键字struct可不可以改成class呢?试一试就知道了:
很显然,编译器给我们报错了。别着急,下面我将一一为大家介绍报错的原因:
首先,面向对象有三大特性:封装,继承,多态
我们要先了解的是什么是封装 :
1.在面向对象中,数据和方法都在类中都放到了一起
2.C++通过访问限定符实现封装
下面要介绍的是访问限定符
1.4类的访问限定符及封装
1.4.1访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
访问限定符说明:
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4.如果后面没有访问限定符,作用域就到类结束。
5. class的默认访问权限为private(structpublic因为struct要兼容C)
这里的第5点class的默认访问权限为private,就是上面代码编译器报错的原因,如果我们将成员函数设置为公有的(或者将成员变量和成员函数都设为公有),编译器就不会报错了:
因此我们定定义类的时候,尽量明确定义访问限定符,不要用class/struct默认的限定符,这样别人在读我们的代码时,一眼便知道成员属性。
1.4.2 封装
封装是一种严格管理,不封装是一种自由管理
在C++中,数据和方法都封装到类里面,可以给你访问的成员定义为共有,不想给你访问的成员定义为私有。
使用封装的好处:
1、我们能修改自己的代码,而不用修改那些调用我们代码的程序片段,从而可以让程式码更容易维护。
2、将相关联的变量和函数封装成一个对象,变量描述对象的属性,函数描述对象的行为,这符合我们对客观世界的认识。
3、还实现了对属性的数据访问限制,也加强了程式码的安全性。
1.5类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类外定义成员,需要使用 : : 作用域解析符指明成员属于哪个类域
在实际开发模块化编程中的使用:
Stack.h文件:
#pragma once class Stack { public: void Init(); void Push(int x); // ... //private: int* _a; int _top; int _capacity; };
Stack.cpp文件:
#include "Stack.h" void Stack::Init() { _a = nullptr; _top = _capacity = 0; }
这样实现了数据与方法的分离。
1.6类对象模型
1.6.1如何计算类对象大小
1.类大小计算遵循结构体对齐原则
2。类的大小与数据成员有关与成员函数无关(空类大小为1个字节),类的大小与静态数据成员无关
3.空类会给一字节,这一字节不存储有效数据,只是为了占位,表示对象存在
情况一:类中既有成员变量又有成员函数
class A1 { public: void f1() {} private: int _a; };
大小:4字节
情况二:类中仅有成员函数
class A2 { public: void f1() {} };
大小:1字节
情况三:类中什么也没有
class A3 { };
大小:1字节