【C++入门】面向对象编程的基石——【类与对象】基础概念篇

⚡ CYBER_PROFILE ⚡
/// SYSTEM READY ///


WARNING \]: DETECTING HIGH ENERGY **🌊 🌉 🌊 心手合一 · 水到渠成** ![分隔符](https://i-blog.csdnimg.cn/direct/60a3de2294e9439abad47378e657b337.gif) |------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| | **\>\>\> ACCESS TERMINAL \<\<\<** || | [**\[ 🦾 作者主页 \]**](https://blog.csdn.net/fengtinghuqu520?spm=1000.2115.3001.5343) | [**\[ 🔥 C语言核心 \]**](https://blog.csdn.net/fengtinghuqu520/category_12955956.html) | | [**\[ 💾 编程百度 \]**](https://blog.csdn.net/fengtinghuqu520/category_13083835.html) | [**\[ 📡 代码仓库 \]**](https://blog.csdn.net/fengtinghuqu520/article/details/147275999?spm=1001.2014.3001.5502) | --------------------------------------- Running Process: 100% \| Latency: 0ms *** ** * ** *** #### 索引与导读 * [一、什么是面向对象编程?](#一、什么是面向对象编程?) * * [OOP的三大特性](#OOP的三大特性) * [二、类的概念与定义](#二、类的概念与定义) * * [2.1)什么是类?](#2.1)什么是类?) * [2.2)类的基本结构与命名规范](#2.2)类的基本结构与命名规范) * * [1)关键字与标识符](#1)关键字与标识符) * [2)分号的必要性](#2)分号的必要性) * [3)成员变量与成员函数](#3)成员变量与成员函数) * [2.3)struct 与 class 的演进与区别](#2.3)struct 与 class 的演进与区别) * * [C 语言的struct vs C++ 中的 struct:](#C 语言的struct vs C++ 中的 struct:) * [2.4)成员函数的 Inline 特性](#2.4)成员函数的 Inline 特性) * [二、访问限定符](#二、访问限定符) * * [1.1)三大访问限定符](#1.1)三大访问限定符) * [1.2)限定符的作用域:管辖范围](#1.2)限定符的作用域:管辖范围) * [1.3)行业通用的"黄金法则"](#1.3)行业通用的“黄金法则”) * [三、类域](#三、类域) * * [:: 作用域操作符](#:: 作用域操作符) * [四、类的实例化](#四、类的实例化) * [五、类的对象大小](#五、类的对象大小) * * * [5.1)成员变量与成员函数的大小存储](#5.1)成员变量与成员函数的大小存储) * [5.2)编译器如何找到函数](#5.2)编译器如何找到函数) * [六、C++类实例化对象的 内存对齐的规则](#六、C++类实例化对象的 内存对齐的规则) * * * [代码示例](#代码示例) * [七、空指针访问规则](#七、空指针访问规则) * * [7.1)绝对禁区:解引用](#7.1)绝对禁区:解引用) * [7.2)调用普通成员函数](#7.2)调用普通成员函数) * [7.3)调用虚函数](#7.3)调用虚函数) * [7.4)静态成员](#7.4)静态成员) * [💻结尾--- 核心连接协议](#💻结尾— 核心连接协议) ## 一、什么是面向对象编程? **面向对象编程** 简称**OOP**,是一种程序设计思想 * 将现实世界中的事物抽象为**对象(Object)** ,并将**数据(属性)**与**处理数据的方法(行为)** 封装在一起 > 如果说**过程化编程(如 C 语言)** 是按照**第一步、第二步、第三步** 的步骤来解决问题,那么`OOP`就是通过 **指挥不同的对象协作** 来解决问题 ### OOP的三大特性 1. **封装 (Encapsulation)** 封装是将数据和操作数据的方法绑定在一起,并隐藏对象的内部实现细节。 * 做法: 使用访问限定符(如 private, public)。 * 好处: 安全性。外部代码不能随便修改对象内部的敏感数据,只能通过预留的接口(方法)进行交互。 2. **继承 (Inheritance)** 继承允许一个类(子类)获取另一个类(父类)的属性和方法。 * 做法: `class Student : public Person { ... };` * 好处: 代码复用。你不需要为每一个新类重写相同的代码。例如,"学生"和"老师"都可以继承"人"这个类的基本属性。 3. **多态 (Polymorphism)** 多态是指同一个接口,在不同的对象上表现出不同的行为。 * **做法:** 通过函数重写或虚函数实现。 * **好处:** 灵活性。你可以写一个通用的函数,比如"让动物叫",传入一只狗它会汪汪,传入一只猫它会喵喵 *** ** * ** *** ## 二、类的概念与定义 ### 2.1)什么是类? **类(Class)** 是**面向对象编程(OOP)** 中的核心概念,它是一种用户定义的数据类型,用于**封装数据(属性)**和**操作数据的方法(函数)** ,是**创建对象的蓝图或模板** *** ** * ** *** ### 2.2)类的基本结构与命名规范 #### 1)关键字与标识符 `class` 是 `C++` 的保留字,用于声明一个新的类型。`Stack` 是用户自定义的类名,建议采用**大驼峰命名法** #### 2)分号的必要性 在 `C++` 中,类定义的末尾必须有一个分号` ;` #### 3)成员变量与成员函数 * **成员变量:** 描述对象的"状态"。 * **成员函数:** 描述对象的"行为" ```cpp //定义一个动态的栈 class Stack { //成员函数 void Init() {} void Push(int x) {} //成员变量 int a; int top; int capacity; }; ``` * **如何去调用这个类里面的功能** ```cpp int main() { Stack s1; Stack s2; s1.Init(); s2.Push(1); s2.Push(2); s2.Push(3); return 0; } ``` *** ** * ** *** ### 2.3)struct 与 class 的演进与区别 #### C 语言的struct vs C++ 中的 struct: * `class` 的**成员默认访问权限** 是` private(私有)` * `struct` 的**成员默认访问权限** 是` public(公有)` `C++`兼容C中的`struct`的用法,同时也将`struct`升级为类 *** ** * ** *** ### 2.4)成员函数的 Inline 特性 **定义在类里面的成员函数默认认为 inline** **什么是 Inline(内联)?** 内联函数告诉编译器在调用该函数的地方直接展开代码,以减少函数调用的开销(如压栈、跳转、返回) > 🔗[Lucy的空间骇客裂缝:inline函数](https://blog.csdn.net/fengtinghuqu520/article/details/156986078?spm=1011.2415.3001.10575&sharefrom=mp_manage_link) **自动内联的条件:** 只要函数体直接写在类声明内部,编译器就会将其视为内联 *** ** * ** *** ## 二、访问限定符 `C++` 一种实现封装的方式,**用类将对象的属性与方法结合在一块**,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用 ### 1.1)三大访问限定符 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/2b8e9d1db1714e769c6f21b28c4b8c12.png) 访问限定符 public private protected > `C++` 有一个`后门`叫 `friend` 关键字,**可以让特定的外部函数或类突破 `private`防线** **(但要慎用,会破坏封装性)** *** ** * ** *** ### 1.2)限定符的作用域:管辖范围 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止,如果后面没有访问限定符,作用域就到`}`即类结束 * **代码示例** ```cpp class Hacker { public: // --- public 的管辖范围开始 --- void attack() {} void defend() {} private: // --- public 结束,private 开始 --- int ip_address; int port; public: // --- private 结束,新的 public 开始 --- void disconnect() {} }; // --- 作用域结束 ``` **虽然 C++ 允许你在类里写无数个 `public`/`private` 块,但行业规范通常建议将同类型的权限归并在一起。通常的顺序是:先写 `public`(方便阅读接口),再写 `private`(隐藏数据)** *** ** * ** *** ### 1.3)行业通用的"黄金法则" 一般成员变量都会被限制为 `private`/`protected`,需要给别人使用的成员函数会放为 `public` * 如果把成员变量设为 `public`,任何人都可以在外部随意修改它: ```cpp // 错误示范 obj.age = -100; // 外部可以直接把年龄设为负数,逻辑崩坏 ``` * 如果设为 `private`,并通过 `public` 的函数来访问,你就可以把关: ```cpp // 正确示范 void setAge(int a) { if (a < 0 || a > 150) { cout << "非法数据!" << endl; return; } _age = a; } ``` * 在 `C++` 中,我们通常提供 `Getxxx()` 和 `Setxxx()` 接口来**控制对私有数据的读写** **后面我们会讲** *** ** * ** *** ## 三、类域 ### :: 作用域操作符 类定义了一个新的作用域,类的所有成员都在类的作用域中。 在类体外定义成员时,需要使用`::`作用域操作符指明成员属于哪个类域 ```cpp class MyClass { public: void printMessage(); // 成员函数声明 private: int data; }; // 在类体外使用 :: 操作符定义成员函数 void MyClass::printMessage() { std::cout << "This is defined outside the class." << std::endl; } ``` **`::`** 明确指定了后续的成员属于哪个类,从而进入了该类的作用域,**是连接成员声明(在类内)与成员定义(在类外)的标准语法** * **代码示例** ```cpp class Stack { public: void Init(); // 成员函数声明 private: int* _array; int _capacity; int _top; }; ``` **错误写法** ```cpp void Init() { _array = nullptr; _top = _capacity = 0; } ``` *编译器错误:找不到`_array`的声明* **正确写法** ```cpp void Stack::Init() { _array = nullptr; _top = _capacity = 0; } ``` *正确:编译器知道在 `Stack` 类的作用域内查找 `_array`* *** ** * ** *** ## 四、类的实例化 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/87e08e2fadb34eeea8a391693ff4b5ed.png) ```cpp #include using namespace std; 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 Date d1; Date d2; d1.Init(2024, 3, 31); d1.Print(); d2.Init(2024, 7, 5); d2.Print(); return 0; } ``` *** ** * ** *** ## 五、类的对象大小 #### 5.1)成员变量与成员函数的大小存储 **对象的大小只包含成员变量的大小,不包含成员函数的大小** * **成员变量** * 类实例化出的每个对象(如 `d1` 和 `d2`),都需要有自己独立的数据空间 * **成员函数(不存储在对象中)** 1. **函数被编译后是一堆指令,这些指令存储在公共的代码段** 2. **对象内部无法直接存储指令代码** 3. 即便是存储函数的指针(地址),也是极大的浪费 > **当你实例化 100个对象 时,就会有 100个重复的指针 指向同一个函数地址** > > 因此,`C++` 设计者决定不在对象中存储成员函数的指针 *** ** * ** *** #### 5.2)编译器如何找到函数 * **普通成员函数** * **普通成员函数(非虚函数)的地址**在编译和链接阶段就已经确定了 * 编译器在生成汇编代码时,直接将其翻译成`call地址`的指令 * 程序运行时,不需要去对象里找地址,而是直接跳转到那个固定的代码段地址去执行 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/05616733326747fdbc80c86c7c817598.png) * **`virtual`虚函数** 如果有虚函数,对象内部确实会增加一个隐含的指针(*虚表指针 `vptr`* ),用来在运行时寻找正确的函数 **后续我们会讲到** *** ** * ** *** ## 六、C++类实例化对象的 内存对齐的规则 **结构体内存对齐规则** 1. **起始位置:** 第一个成员在与结构体偏移量(offset)为 0 的地址处 2. **成员对齐:** 其他成员变量要对齐到该成员的 对齐数 的整数倍地址处 * 对齐数 = 编译器默认的一个对齐数(VS中默认为 8,Linux/gcc 默认为成员大小本身)与该成员大小的 较小值 3. **总大小计算:** 结构体的总大小必须是 最大对齐数 的整数倍 * 最大对齐数 = 所有成员变量(包括嵌套结构体成员)中最大的那个对齐数 4. **嵌套结构体:** 如果嵌套了结构体,嵌套的结构体要对齐到 它自己内部最大对齐数 的整数倍处。结构体的整体大小则是所有最大对齐数(含嵌套结构体的对齐数)的整数倍 #### 代码示例 ```cpp #include using namespace std; // 场景 A:含有成员变量和成员函数 class A { public: void Print() { cout << _ch << endl; } private: char _ch; // 1 字节 int _i; // 4 字节 }; // 场景 B:仅含有成员函数,没有成员变量 class B { public: void Print() { // ... } }; // 场景 C:完全的空类 class C {}; int main() { A a; B b; C c; cout << "Size of A (char + int): " << sizeof(a) << endl; cout << "Size of B (only function): " << sizeof(b) << endl; cout << "Size of C (empty class): " << sizeof(c) << endl; return 0; } ``` **类 A 的大小分析** 针对类 `A` 包含一个 `char` 成员和一个 `int` 成员的情况,其内存分配逻辑如下: * **成员分析** :类 `A` 有一个 `char` (1字节) 和一个 `int` (4字节)。成员函数 `Print` 不占对象内存。 * **对齐过程** : 1. `_ch` 放在偏移量 **0** 处。 2. `_i` 是 `int`,大小为 4。默认对齐数是 8,取较小值 **4** 。因此 `_i` 必须对齐到 4 的倍数地址。 3. 偏移量 1, 2, 3 不满足要求,被填充(Padding),`_i` 从偏移量 **4** 开始存储,占用 4, 5, 6, 7。 * **总大小计算** :当前占用 0 到 7 共 8 个字节。最大对齐数是 4(`int` 的大小),8 是 4 的倍数。 * **结果** :`sizeof(a)` 为 **8**。 *** ** * ** *** **类 B与类 C 的大小分析** 类 `B` 虽然有函数,但没有成员变量;类 `C` 既没有函数也没有变量。 * **计算逻辑** : * **对于类 B** :成员函数存储在代码段,不占用对象空间。逻辑上它和类 `C` 一样,都是"空"的。 * **关键点** :如果空类的大小为 0,当我们声明 `C c1, c2;` 时,这两个对象就会拥有相同的内存地址。为了区分不同的对象,编译器会给空类实例分配一个**占位符**。 * **结果** :在绝大多数编译器(如 VS, GCC)中,`sizeof(b)` 和 `sizeof(c)` 均为 **1**。 > **注意**:这 1 个字节不存储任何有效数据,仅用于标识对象的存在 *** ** * ** *** ## 七、空指针访问规则 * 下⾯程序编译运⾏结果是() ```cpp #include using namespace std; class A { public: void Print() { cout << "A::Print()" << endl; } private: int _a; }; int main() { // 1. 定义一个空指针 p A* p = nullptr; // 2. 试图通过空指针调用成员函数 p->Print(); return 0; } ``` 只要`Print`函数内部不访问对象的成员变量(不解引用 this),空指针调用成员函数是合法的 ### 7.1)绝对禁区:解引用 * **形式:** `*ptr` 或 `ptr->memberVariable` * **结果:** ❌ 崩溃(导致未定义行为UB) *** ** * ** *** ### 7.2)调用普通成员函数 调用成员函数时,`ptr` 会作为 `this` 指针传给函数 * **情况 A:函数内访问了成员变量** * 代码试图通过`this->variable`访问内存。由于`this`是`nullptr`,这等同于解引用。 * 结果:❌ 崩溃。 * **情况 B:函数内没有访问任何成员变量** * 函数体内不需要解引用`this`指针。虽然在`C++`标准中这属于未定义行为(UB),但在大多数编译器实现下,程序能正常运行并输出结果。 * 结果:⚠️ 通常能运行(但属于 UB,不推荐依赖) *** ** * ** *** ### 7.3)调用虚函数 虚函数的调用依赖于**虚函数表指针(`vptr`)** ,而 `vptr` 存储在对象内存的头部 * **形式:** `ptr->virtualFunc()` * **原理:** 程序需要先去`ptr`指向的内存里找`vptr`,因为 `ptr` 是空的,找不到内存 * **结果:** ❌ 崩溃 *** ** * ** *** ### 7.4)静态成员 **静态成员函数** 和**静态成员变量** 属于**类**而不属于对象 * **形式:** `ptr->staticFunc()` * **原理:** 编译器会将其优化为 `ClassName::staticFunc()`,根本不会去解引用 `ptr` * **结果:** ✅ 安全,正常运行。 *** ** * ** *** ## 💻结尾--- 核心连接协议 **警告:** 🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠 *** ** * ** *** **【📡】 建立深度链接:** **关注**本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。 **【⚡】 能量过载分发:** 执行**点赞**操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。 **【💾】 离线缓存核心:** 将本页加入**收藏**。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。 **【💬】 协议加密解密:** 在**评论区**留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。 **【🛰️】 信号频率投票:** 通过**投票**发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。 *** ** * ** *** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/57b03915c54b43a7a03fa92dbbfe57c3.gif) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0905dc972de8414bb602715de3f866ee.gif)

相关推荐
MSTcheng.2 小时前
【C++】链地址法实现哈希桶!
c++·redis·哈希算法
重生之后端学习2 小时前
25. K 个一组翻转链表
java·数据结构·算法·leetcode·职场和发展
段子子2 小时前
【使用MQTT】
java
你撅嘴真丑2 小时前
第五章 C++与STL入门
开发语言·c++
坐在地上想成仙2 小时前
从机床到键盘:用机械设计思维写出一个可部署网页
java·c++·python
ros2752292 小时前
idea & gitee 使用教程
java·gitee·intellij-idea
叫码农就行2 小时前
spring cloud 笔记
java·笔记·spring cloud
CoderCodingNo2 小时前
【GESP】C++五级练习题 luogu-P2242 公路维修问题
开发语言·c++·算法
青云交2 小时前
Java 大视界 -- Java 大数据机器学习模型在电商商品推荐系统中的冷启动问题攻克与个性化推荐强化
java