| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> 【C++要笑着学】类和对象 | 初识封装 | 访问限定符 | 类的作用域和实例化 | 类对象模型 | this指针 -> 正文阅读 |
|
[C++知识库]【C++要笑着学】类和对象 | 初识封装 | 访问限定符 | 类的作用域和实例化 | 类对象模型 | this指针 |
?🔥 🔥 🔥 🔥 🔥??火速猛戳订阅?👉??《C++要笑着学》? ?👈?趣味教学博客?🔥 🔥 🔥 🔥 🔥 ? ?[ 本篇博客热榜最高排名:13?] ? 写在前面:?我是柠檬叶子C,我又来了!本章将正式开始学习C++中的面向对象,本篇博客涵盖讲解 访问限定符、封装的基础知识、类的作用域和实例化、探究类对象的存储和对于this指针由浅入深地讲解。如果觉得不错,可以 "一键三连" 支持一下博主! 你们的关注就是我更新的最大动力!Thanks ? (・ω・)ノ Ⅰ. 面向对象0x00 初步认识
?举个栗子,比如设计简单的外卖系统 ~ 面向过程:关注实现下单、接单、送餐这些过程。体现到代码层面 -- 方法/函数 面向对象:关注实现类对象及类对象间的关系,用户、商家、骑手以及他们之间的关系。体现到代码层面 —— 类的设计及类之间的关系。 ??再举一个比较下饭的例子! 面向对象:狗吃屎 —— 关注于谁去吃屎,狗去吃屎的,狗这个对象,对象有一个动作是吃狗屎。 面向过程:吃狗屎 —— 是一个动作,关注于怎么去吃狗屎。 ?有人说:面向过程不太适合去实现复杂的系统。 ? 但是我个人认为这个说法是不准确的,比如 Linux 操作系统就是由纯C语言写的。 C++ 是基于面向对象的,支持面向过程和面向对象 "混编" 。 原因是 C++ 兼容 C,这个我们在本章也会慢慢探讨。 ?但是像 Java 这样的语言是纯面向对象。 只有面向对象,就算你想实现一个排序也要写一个类出来…… 0x01 类的引入📚 我们来由浅入深地引入一下,我们先做一个的了解: 在C语言中,结构体中只能定义变量…… ? 而在C++中,结构体内不仅可以定义变量,还可以定义函数! 💬 我们先来试着写一个最简单的类:
?诶!这不就是 C语言 的结构体吗? ? 确实!但是在 C++ 里,struct 也跟着升级成了类。? ?我们可以使用 struct 或 class 来定义类,它们之间有什么区别我们后面再说。 💡 因为 C++ 兼容 C 里面结构体的用法,同时 struct 也在 C++ 中升级成了类。 💬 所以 C++ 就可以直接使用类名来定义了:
🔑 解读: ? 我们既能用 struct Student s1 来定义,还能直接使用 Student s2,通过使用类名直接定义。这体现了 C++ 兼容 C 的特点! ?但是如果这是在 C语言 里, stuct Student 才是它的类型,直接使用 Student 定义是不可以的(当然,用 typedef 就另当别论了)。 🔍 我们通过调试模式来看看: 它其实就是一个结构,你可以理解成和我们之前学的结构体是 "一样的" ,只是定义的方式既兼容了 C 还兼容了 C++ ,但是下面我们还会认识到一些它的不同之处,我们继续往下看。 ?除了类名的定义,还有什么新的玩法呢? 如果是在C语言里,结构体里只能定义变量,就是一个多个变量的集合。 💬 如果我们想要将 s1 中的变量进行初始化,我们还得一个个写,真的是很麻烦:
?但是在C++里,我们不仅可以定义变量,还可以定义函数(方法)。 💬 我们可以定义一个 "初始化" 函数:
💡 我们在 C++ 中一般称这些变量为成员变量,称这些函数为成员方法。 ?这个时候似乎发现了一些新的问题! 我们的这个参数名取的好像和成员变量里一样了,比如我们访问 name 的时候到底是成员变量里的 name 还是成员方法里的 name 呢? ?这就让人区分不开了…… 为了能够更好的区分哪个是成员变量,我们在定义成员变量名时可以给它们做一些标记: 下面是几种常见的表示为成员变量的 "风格" :
?这个并没有明确的规定,不同的公司也有不同的风格。 我个人喜欢用前面加杠的方式来区分,看上去比较显眼。 💬 这样就可以区分开来了:
?为了方便测试,我们再来写一个简单的打印函数:
?💬 我们来调用它们进行一个打印:?
🔺 总结:C++ 对我们的 struct 进行升级了,升级为类了。它兼容以前的用法,又有了新的用法。 0x02 class 关键字?我们刚才引入部分讲了 struct?,我们知道了它在 C++ 里升级成了类。其实 C++ 也有自己的亲儿子,就是 class,我们现在就来好好地讲一讲这个 class 。 📚 语法: ① class 为定义类的关键字,className 为类的名字。 ② 大括号中内容为类的主题,注意类定义结束时后面要加分号! 💬 我们先试着把刚才写的代码改成 class:
啊这,我们换成 C++ 亲儿子 class 居然报错了,这又是为什么呢? ?因为 C++ 讲究 "封装" …… C++ 这里是它把数据和方法都放在了类里面。 这和C语言是不同的,C语言里数据是数据,方法是方法。 ?这里我们就来提一下 面向对象的三大特性:封装、继承、多态。 📚 我们先来重点看一下这个?封装 : ① 数据和方法都被放在了一起。 ② 访问限定符 就是因为这个访问限定符,所以这里我们报错了,我们下面来学习一下访问限定符。 Ⅱ. 类的访问限定符及封装0x00?访问限定符📚 C++ 实现封装的方式:用类将对象的属性与方法结合在一起,让对象更加完善,通过访问权限选择性地将其接口提供给外部的用户使用。 一共有三种访问限定符,分别是 public(公有)、protected(保护)、private(私有)。 这一听名字就能知道,公有就是随便玩,保护和私有就是藏起来一点点不让你随便玩得到。 访问限定符说明 ① public 修饰的成员,可以在类外面随便访问(直接访问)。 ② protected 和 private 修饰的成员,不能在类外随便访问。 ? (此处 protected 和 private 是类似的,现在你可以认为他们是一样的,后面我们讲继承的时候才能体现出它们两的区别) 这就分出了两个阵营,一个阵营是可以随便访问的,一个阵营是不能随便访问的。 ③?class 的默认访问权限为 private,struct 为 public ! 这就是为什么我们刚才编译会报错,因为 class 默认访问权限是 private! 而之前我们用 struct 没有问题,是因为 struct 默认访问权限是 public 共有的。 ?好家伙,这下破案了!原来刚才报错的原因是 class 默认是私有的, ? 那好,既然知道问题所在了,我们该如何解决让它成功访问呢? ?? 💡 使用访问限定符就可以了! 💬 使用我们的访问限定符,加一个 public :
?搞定!我们再来细说一下刚才加进去的访问限定符。 📚 在这之前我们再说两个知识点: ③ 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。 ④ 如果后面没有访问限定符,作用域就到 {?类结束。 ?也就是说,我们刚才加进去的 public?, 从它开始到下一个访问限定符出现为止的这块范围,都是共有的了, 但是因为后面没有再次出现访问限定符,所以作用域就到类结束为止,我们看范围图: 💬 我们再加一个访问限定符 private 进去看看:
现在,?public 能影响到的范围就到 private 出现前为止了,我们再来看看它们的范围图: ?这,就是访问限定符在这里起到的一个作用。 📌 注意事项: ① 访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。 ② 我们一般在定义类的时候,建议明确定义访问限定符,不要用 struct / class 的默认的访问权限,就像这样:
虽然你不指定他会有默认限定,但是还是建议你明确写出来, 因为这样能让人一眼就看出它是共有的还是私有的。 0x01?什么是封装📚 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来实现对象进行交互。 ① 把数据都封装到类里面。 ② 可以给你访问定义成公有,不想给你访问的定义成私有或者保护。 0x02?封装的意义? 封装的意义是什么? ?封装是一种更好的严格管理,不封装是一种自由管理。 ? 那么是严格管理好,还是自由管理好呢? ?举一个最简单的例子: 比如最近的疫情,漂亮国单日新增一百万,你说是自由的管理好呢?还是严格的管理好呢? 我们和漂亮国其实都是在控制疫情的,但是我们是动态清零的政策,是非常严格的管理。 ?而漂亮国是自由管理,虽然人人高呼 "Freedom" ,但是疫情一直都难以得到控制。 0x03?封装的本质📚 封装的本质是一种管理。 我们是如何管理疫情的呢? 比如进商场,如果疫情期间没有人管理, 让大家都随意进,那疫情就不好控制了。 所以我们要对商场进行很好的防疫管理措施! 那么我们首先要把商场 "封装" 起来,你想进入商场就必须要扫健康码。 并不是说不让你进商场,而是你必须要走正门扫码才可以进入。 通过扫码,是绿码你才能进商场,在疫情防疫合理的监管机制下进商场。 类也是一样,我们使用类数据和方法都封装到了一起,不想让人随意来访的, 就是用 protected / private 把成员封装起来,开放一些共有的成员函数对成员合理的访问。 ?所以,封装是一种更好、更严格的管理! Ⅲ.? 类的作用域和实例化0x00 类定义的两种方式① 申明和定义全部放在类中
📌 注意事项: 成员函数如果在类中定义,编译器可能会将它当作内联函数来处理。 注意,是可能。并不是说一定会成为内联函数,之前讲内联函数的时候我们也说了。 内联函数对编译器来说也只是一个建议。至于到底会不会成为内联是由编译器来决定的。 这也不取决于编译器心情,心情好就让你成为内联,心情差就不让…… 而是!取决于编译器看这个函数符不符合条件,一般一个函数太长(大概是十几行左右), 或者函数是一个递归,编译器就不会让它成为内联了。 ② 声明和定义分离: 💬 test.h:
💬 test.cpp:
?诶,这函数名前的 : : 是什么?我们继续往下看~ 0x01?类的作用域类定义了一个新的作用域,类的所有成员都在类的作用域中。 在类外定义成员,需要使用作用域解析符?:?: 来指明成员属于哪个类域。? ?比如说我们写一个比较正式一点的项目(申明和定义分离) 💬 Stack.h:
💬 Stack.cpp:
0x02?类的实例化首先要说清楚的是:类本身是没有存储空间的。 通过类建立出对象,即实例化,才会有实际的存储空间。 📚 我们把用类类型创建对象的过程称为 —— 类的实例化。 ① 类只是一个像 "图纸" 一样的东西,限定了类有哪些成员。定义出一个类,并没有分配实际的内存空间来存储它。 ② 一个类可以实例化出多个对象,占用实际的物理空间,存储类成员的变量。 💡 举个例子: 类实例化对象就像是在现实中使用设计图建造房子,类就像是设计图。 你可以根据这个设计图造出很多栋楼出来。 只有楼建好了里面才能住人,你才能放家具、放东西进去。 设计图能住人吗?当然是不能!因为并没有分配实际的内存空间。 只有你照着设计图去建造,即实例化出对象,占用实际的物理空间,才能住人。 Ⅳ.? 类对象模型0x00 计算类的存储大小?类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?我们该如何计算一个类的大小呢? ? 比如这个栈和它定义出来的对象是多大呢? 💬 Stack.h
💬 Stack.cpp
?对象中存了成员变量,是否存了成员函数呢?没存成员函数! 计算类或类对象的大小只看成员变量! 并且要考虑内存对齐,C++内存对齐规则和C结构体一致。 0x01 类对象的存储方式猜测① 对象中包含类的各个成员: ?缺陷:每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次会浪费空间。 ② 只保存成员变量,成员函数存放在公共的代码段: ? 对于上述两种存储方式,那计算机到底是按照哪种方式来存储的呢?
? A2 没有成员变量,A3 更是什么都没有,为什么大小是 1 呢?为什么不是 0 呢?
💡 我们尝试给 aa 对象 和 bb 对象取地址,它们是有地址的, 取地址就是要拿出它存储空间的那块,所以这里总不能给一个空指针吧? 如果大小给 0 的话就没办法区分空间了。 所以,空类会给 1 字节,这 1 字节不存储有效数据,只是为了占个坑,表示对象存在。 🔺 结论:一个类的大小,实际就是该类中成员变量之和。当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。 0x02 内存对齐规则?C++ 内存对齐规则和 C语言 中的结构体内存对齐规则是一样的。 (以下内容是C语言教学部分的内容,如果没印象了可以复习一下) 💬 我们先来观察下面的代码:
? 为什么是12呢?这就涉及到结构体内存对齐的问题了。 📚 结构体的对齐规则: ① 结构体的第一个成员放在结构体变量在内存中存储位置的0偏移处开始。 ② 从第2个成员往后的所有成员,都要放在一个对齐数(成员的大小和默认对齐数的较小值)的整数的整数倍的地址处。VS 中默认对齐数为8! ③ 结构体的总大小是结构体的所有成员的对齐数中最大的那个对齐数的整数倍。 ④ 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。 📌 注意事项:VS 中默认对其数为8,Linux中没有默认对齐数概念! Ⅴ.? this指针0x00 this指针的引出💬 为了能够更好地讲解,我们首先来定义一个日期类 Date:
? 这里我们思考一个问题: Date 类中有 Init 和 Print 两个成员函数,函数体中没有关于不同对象的区分,那当 d1 调用 Print 函数时,这个 Print?函数是如何知道要打印?d1 对象的?而不是去打印?d2 对象呢?? ?因为 C++ 在这有一个隐藏的东西 —— this 指针! 💡 C++ 通过引入 this 指针解决该问题。 📚?C++ 编译器给每个 "非静态的成员函数" 增加了一个隐藏的指针参数, 让该指针指向当前对象(函数运行时调用该函数的对象),它是系统自动生成的, 在函数体中所有成员变量的操作,都是通过该指针去访问。 只不过所有的操作对程序员来说是透明的, 就是不需要程序员自己来传递,编译器自动帮你去完成。 💬 当然,Init 也会被处理(注释的是隐藏前的内容):
0x01 this 使用细则📌 注意事项:this 是作为一个关键字存在的 ① 调用成员函数时,不能 "显示地" 传实参给 this : ② 定义成员函数时,也不能 "显示地" 声明形参 this : ③ 但是,在成员函数内部,我们可以 "显示地" 使用 this : 也就是说,你不写 this 他会自动加,你写了他也是允许你写的。 ? 这是为什么呢? 因为有些地方我们用得到这个 this,这个我们下一章会讲。(返回本体) 虽然可以 "显示地" 用,但是一般情况下我们都不会自己 "显示地写" 。 因为没有必要,你不写他也会自动加上去的:
?但是你想写也没人会拦你,简单说就是 ——?? ? 0x03 this指针中的const📌 注意事项: this 指针是不能被改变的。 ?因为考虑到这是第一次讲解 this 指针,为了方便由浅入深地讲解, 我们选择了 ——?战术装瞎 ,忽略了 this 指针中的 const 来讲解的。 ?其实 this 指针还被 const 修饰。 this 指针的本质是一个常量指针,是通过 const 修饰 this 指针指向的内存空间。 💬 不信?你改改看: ?? 💬 写得更准确些(注释的是隐藏前的内容):
0x04?this指针的特性① this 指针的类型:? 类类型* const ② this 指针只能在 "成员函数" 的内部使用。 ③ this 指针本质上是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给 this 形参。所以对象中不存储 this 指针。 ④ this 指针是成员函数第一个隐含的指针形参,一般情况由编译器通过 ecx 寄存器自动传递,不需要用户传递。 参考资料: Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. . 百度百科[EB/OL]. []. https://baike.baidu.com/. 比特科技. C++[EB/OL]. 2021[2021.8.31]. .
本章完。 |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 4:20:26- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |