ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C++篇:类和对象(上)

C++篇:类和对象(上) 目录1.面向过程和面向对象初步认识2.类的引入3.类的定义3.1 声明和定义分离3.2 声明和定义放在一块4.类的访问限定符4.1封装5.类的实例化6.类对象模型大小6.1 类大小计算6.2 结构体内存对齐规则7.this指针7.1 this指针的引入7.2 this的注意事项这次算是真正踏入 C 的门槛了之前都是大部分可以和 C 兼容而类和对象是 C 的特性需要认真学习这一 part 也是比较难的。这次要学习的内容是把面向对象和面向过程有详细的认识然后去了解什么是类类的定义如何去比喻一个类类的访问限定符等等。什么是封装什么是类的实例化以及类的实例化怎么用等等这些都是去学习讨论的一点点来C 就是如此前期学习的会比较困难稍微后面就会相对轻松一点。那么话不多说立马开始吧。1.面向过程和面向对象初步认识明确一点C 是面向过程的它更在意的是过程C 是面向对象关注的是对象。举个例子我们平常生活中肯定会有点外卖吧从过程上来看是这样app 上架商品客户打开 app 在上架下单然后系统派单骑手接单送餐客户签收。这些过程有哪些呢上架下单派单接单送餐签收等等过程就是而 C 语言就是面向过程的注重步骤。而 C是重点面向对象即谁下单谁送货谁出货谁搭建沟通平台。那就是商户客户骑手企业app。将一个过程分成不同的对象靠对象之间的交互来完成人们不需要知道餐从哪里来怎么炒的菜系统派单逻辑花了多久等等过程这就是面向对象。类就是现实世界映射到虚拟计算机系统里。2.类的引入C 里类除了放着变量还可以放置函数C 语言中的结构体中只能存放变量而在 C 中不仅可以存放变量还能存放函数体。比如 C 语言中的栈结构体中只放变量而在 C 中不但能放变量还能放函数。如下就是 C 里的类会晓得原来 struct 里面也可以定义函数而不是单单定义变量那么单一。struct stack { void init(size_t capacity) { _a (int*)malloc(sizeof(int) * capacity); if (_a nullptr) { perror(defeat); return; } _size 0; _capacity capacity; } int* _a; size_t _capacity; size_t _size; void push(int x) { _a[_size] x; _size; } int top() { return _a[_size - 1]; } void Destroy() { if (_a) { free(_a); _a nullptr; _capacity 0; _size 0; } } };int main() { stack s1; s1.init(10); s1.push(2); s1.push(3); s1.push(8); s1.push(9); s1.top(); s1.Destroy(); }当然要注意现在你是可以运行的编译可以通过但是如果你把 struct 变成 class就会报错。原因在后面会提其实在 C 中更喜欢用 class 来代替啥是 class马上就说。3.类的定义这里正式介绍一下什么是类。类有个关键字还要有类名{}里的是主体可以包含函数变量定义类定义后面}有个分号不能省。类体中内容称为类的成员变量称为成员变量;函数称为成员函数。class wj { public: void Print(const inta,const intb) { cout a b; } int x; int y; char c; };这就是一个很普通的类首先类体中要用成员函数和成员变量组成最后有一个分号。然后需要注意的是要类可以两种定义方式一种是声明和定义分离一种是直接在类里面定义。3.1 声明和定义分离即类体中成员函数的声明放在h文件中类的成员函数放在cpp文件中注意这样子成员函数前需要类名声明和定义分离3.2 声明和定义放在一块声明和定义合并这是两种不同的实现方式不过一般的我会采取第二种更为方便管理。4.类的访问限定符类的访问限定符有publicprotectedprivate看中文意思能够理解是公开的、受保护的以及私有的。这也涉及到封装的概念C 就是把类将对象的属性和实现方法结合在一起用封装的方式决定哪些部分对用户开放这个封装的方式就叫访问限定符。public 是公共区域它所修饰的成员可以在类外直接被访问。而 protected 和 private 所修饰的成员是不能被类外直接访问的。这两个类似。访问限定符的作用域从该限定符出现开始到另一个限定符出现为止如果没有就是以 } 结束为标志代表类的结束比如 public开始.....到 private出现那么中间的就是 public 的区域。class 默认为私有privatestruct 默认是公开public因为要兼容 C所以也解释了上面为何用 struct 会编译通过而 class 就报错。这里提问一个经典问题C中class和struct的区别答C 需要兼容 C 语言所以 C 中 struct 可以当成结构体使用还可以用来定义类这个和 class 定义类是一样的区别是 struct 定义的类默认访问权限是 publicclass 定义的类默认访问权限是 private。4.1封装封装是指通过类将数据以及操作数据的方法有机结合把对象的具体属性和实现方式关起门来只让你调用接口来实现用户的目的。封装的好处是减少不必要的麻烦为了更好的服务于开发商和用户。比如玩一个游戏所有的游戏实现逻辑你是看不到的玩家只需要鼠标、键盘、耳机、一个显示屏就可以了而里面的代码运行、CPU 处理玩家的鼠标键盘、服务器传输数据、传回画面进行反馈这些都不是我们需要考虑的。再举个例子对于计算机使用者而言不用关心内部核心部件用户只需要知道怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。不然使用者出于好奇心动了一些部件导致损坏那就会造成很多麻烦专业的事由专业的人来做。4.类的作用域类域是新的作用域在类里面类的所有函数成员、成员变量都在类的作用域里在类外定义成员需要说明这是我这个类的成员就是要类名 :: 来表示主权::是域作用操作符。class Person { public: void PrintPersonInfo(); private: char _name[20]; char _gender[3]; int _age; }; // 这里指定PrintPersonInfo是属于Person这个类域 void Person::PrintPersonInfo() { cout _name _gender _age endl; }5.类的实例化实例化是用类创建实体对象的过程称之为类的实例化很形象的比喻就是图纸到实物的过程图纸就是类而实物枪械零件手办汽车的实体这些具象化的东西是实例化我们的一切操作都是在实物上完成。class wj { public: void Print(const inta,const intb) { cout a b; } int x; int y; char c; }; int main() { wj Wj; wj.Print(1,4);//错误没有空间编译失败语法错误 Wj.Print(1,4);//√ }主函数的第一个代码就是类的实例化就是要给我一个对象其次在这上面进行一系列操作。时刻记住类是对对象进行描述的是一个图纸但是你说图纸能不能放东西能不能去实际使用那显然是不能。但是实例化之后是可以的就是你把图纸里的东西造出来的那么就可以使用了。可以进行装饰拆卸加装零件等等在类里面类实例化出的对象可以访问可以存放类成员变量占用实际的物理空间。做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子类就像是设计图只设 计出需要什么东西但是并没有实体的建筑存在。类可以有多个实例化ps注意一个点我们已经知道不能直接用类去访问因为那个是声明无法存储数据。那么我们实例化出来的对象1对象2他们的地址一样吗我们每个对象的成员函数地址一样吗即我们栈1要压栈和栈2要压栈操作是用的同一个函数代码进行压栈操作吗是的push是公共区域stack s1; stack s2; s1.init(10); s2.init(10); cout s1对象地址 s1 endl; cout s2对象地址s2 endl; void(stack:: * ppush)( const int) stack::push; cout push函数成员地址 (void*)ppush endl;6.类对象模型大小6.1 类大小计算类里面有类的成员变量还有类的成员函数那么如何计算类的大小呢要不要把类的成员函数算进去呢下面这个类大小是多少其实就是在想到底是都算进去还是只算成员变量把成员函数放在公共代码段不去计算。class A1 { public: void PrintA() { cout_aendl; } private: char _a; }; class A2 { public: void PrintA(){ } } class A3 { }结论是一个类的大小是该类中的成员变量的总和。只保存成员变量成员函数当然你得注意一个规则这个在c语言中struct一样规则当有多个不同类型的变量时需要遵照内存对齐规则。注意空类的大小空类比较特殊编译器给了空类一个字节来唯一标识这个类的对象。为啥这就是规则也可以理解为人没来给book一个位置。6.2 结构体内存对齐规则第一个成员在与结构体偏移量为0的地址处。其他成员变量要对齐到对齐数的整数倍地址数对齐数编译器默认的对齐数与该成员的较小值vs默认对齐数是8结构体总大小最大对齐数结构体内最大变量类型的大小和默认对齐数取最小值的整数倍既然复习了一下结构体的内存对齐规则就来看看类A1为什么是8首先char类型的字节是1这个是毫无疑问的int类型的字节大小是4这个也没有意外。先是char再是int的话。根据结构体内存对齐规则char就直接占一个字节然后int要对齐到对齐数8和4比较小值是4所以对齐数是4的整数倍4或者8那就是从5开始占据4个字节前面为了对齐就要浪费3个字节。加起来就是8那么有个核心问题为什么要进行内存对齐答首先内存对齐是指在内存中按照硬件访问数据的要求把数据放在特定的边界上。如果不进行内存对齐读取数据会很麻烦对齐也许只要读1次读2次就会读取完的数据没有内存对齐后会增加读取次数浪费时间。这在计算机喜欢以空间换时间的性格是相违背的。而现在cpu的寄存器决定了内存内访问的基本粒度。例如64位cpu寄存器通常为8字节这表示cpu会以8字节为单位进行内存访问这是基于对硬件效率的考虑是为了降低访问延迟。提高cpu的访问效率如果不对齐就要进行额外的读取操作浪费时间7.this指针7.1 this指针的引入来看一个date类的class Date { public: void Init(int year, int month, int day) { _year year; _month month; _day day; } void Print() { cout _year - _month - _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1, d2; d1.Init(2022, 1, 11); d2.Init(2022, 1, 12); d1.Print(); d2.Print(); return 0; }这里产生一个疑问为什么我们调用的init和print是同一个函数那编译器是怎么区分他们的数据打印结果完全不同。函数调用都是同一个代码片段你怎么知道一个要打印d1的数据d2的数据答案是它会传递一个我们用户看不到也不需要看到的隐藏指针this指针。是编译器自己改的而我们看不到让指针去指向调用函数时的对象。这是我们看到的这是编译器做的但是this指针并不能在形参和实参中显示传递7.2 this的注意事项this指针存在栈里因为它是隐式的作为形参和实参传递到函数。所以this指针在栈里不在堆区也不在常量区最多只能在成员内部使用不能在别的地方显示。vs中对象地址存放在ecx寄存器中而寄存器存储thsi指针的值。针对第1点和3点就是this指针是形参类的实例化对象调用成员函数时对象地址会作为参数传给this的形参即对象本身不存this指针。this就是对象的地址。d1和d2的对象地址不一样再有一个问题就是this指针可以为空吗this可以为空但是不能去访问this的内容会出错。预告后面会单开一个部分专门学习一个比较难的地方是类的默认成员函数其中最主要构造函数和析构函数然后拷贝构造和赋值重载这里是块硬骨头学会了这里其实又是一个小进步。
返回列表