总前置:C 语言 面向过程 VS C++ 面向对象 核心对比(核心认知)
1. 编程思想本质区别
| 维度 | C 语言(面向过程 POP) | C++(面向对象 OOP) |
|---|---|---|
| 核心思路 | 以函数 / 流程为中心,拆分步骤,关注「怎么做」 | 以类 / 对象为中心,拆分实体,关注「谁来做、有什么属性」 |
| 数据与函数关系 | 数据(结构体 struct)和函数完全分离,函数通过参数操作结构体数据,无绑定关系 | 类封装属性(成员变量)+ 行为(成员函数),数据和操作绑定为整体 |
| 访问权限 | 无权限控制,结构体成员全局可读写,极易误改数据 | 私有 / 保护 / 公有三大访问控制,封装保护内部数据 |
| 代码复用 | 函数复用、结构体复制;无继承机制,重复代码多 | 继承、多态、复用类,大幅减少冗余代码 |
| 内存管理 | 手动 malloc/free,无构造 / 析构,变量创建无自动初始化 | 构造函数自动初始化、析构自动释放资源,支持 RAII 自动管理内存 |
| 复合数据类型 | 仅基础结构体,不能包含函数,不支持联合体封装、常量成员 | 类、结构体、联合体统一支持成员函数、权限、构造、继承 |
2. 最简示例对比:学生成绩存储
C 语言实现(分离式)
#include <stdio.h> // 只有数据,无操作函数 struct Student { char name[20]; int score; }; // 函数独立在外,需要传结构体指针操作数据 void showStu(struct Student* s) { printf("姓名:%s,分数:%d\n", s->name, s->score); } int main() { struct Student s = {"张三", 90}; showStu(&s); return 0; }问题:score可以在任意地方随意修改,无法校验分数 0~100;数据和打印逻辑割裂。
C++ 类实现(封装一体)
#include <iostream> #include <string> using namespace std; class Student { private: string name; int score; // 私有数据,外部不能直接修改 public: // 构造函数自动初始化 Student(string n, int s):name(n),score(s){ if(score<0||score>100) score=0; // 数据校验 } // 成员函数绑定数据 void showStu() { cout << "姓名:" << name << ",分数:" << score << endl; } // 提供安全接口修改数据 void setScore(int s){ if(s>=0&&s<=100) score=s; } }; int main() { Student s("张三",90); s.showStu(); s.setScore(95); s.showStu(); // s.score = 200; 编译报错,私有成员禁止外部访问 return 0; }优势:数据被保护,修改必须走校验接口,数据和行为绑定。
第一大部分:C++ 类与对象 完整核心知识点(基础必学)
1. 类(Class)与对象(Object)基础定义
(1)概念
- 类:抽象模板,描述一类事物共同的属性(成员变量)和行为(成员函数),相当于 C 语言结构体的超级增强版。
- 对象:类的实例,根据类模板创建出具体实体,占用内存;一个类可以实例化无数对象。
(2)类完整语法结构
class 类名 { // 访问控制符:控制外部代码能否访问成员 public: // 公有:任意地方可访问 // 成员函数(对外接口) private: // 私有:仅本类内部成员函数可访问(默认权限) // 成员变量(核心数据,封装隐藏) protected: // 保护:本类+子类可访问,外部不可(继承专用) };(3)C struct VS C++ class 关键差异(核心区分点)
- C 语言
struct:默认全部公有,只能存数据,不能写成员函数; - C++
struct:本质和 class 几乎一致,默认公有,支持成员函数、构造、继承; - C++
class:默认私有,工程开发优先用 class 做封装。
示例:
// C++ struct 默认公有 struct StuStruct { int score; }; // C++ class 默认私有 class StuClass { int score; }; int main() { StuStruct s1; s1.score = 90; // 合法,默认public StuClass s2; // s2.score = 90; 编译报错,默认private return 0; }2. 类的三大核心特性(OOP 核心精髓)
特性 1:封装(Encapsulation)
- 含义:将数据和操作数据的方法打包到类,隐藏内部私有细节,只对外暴露安全接口。
- 目的:
- 保护数据,防止非法修改;
- 简化调用,使用者不用关心内部实现;
- 实现手段:
private私有成员 +public读写接口(get/set 函数) - 对比 C 语言:C 无封装,结构体数据裸漏,无保护机制。
标准 get/set 模板:
class ExamInfo { private: int percent; public: // 获取值 int getPercent() { return percent; } // 安全设置值,附带校验 void setPercent(int p) { if(p >=0 && p <=100) percent = p; else cout << "分数非法!" << endl; } };特性 2:继承(Inheritance)
- 含义:一个类(子类 / 派生类)复用另一个类(父类 / 基类)的所有公有、保护成员,扩展新功能。
- 对比 C 语言:C 语言无继承,代码复用只能复制粘贴,冗余严重。
- 基础语法:
class 子类 : 继承方式 父类 {};
// 父类 class Person { protected: string name; public: void showName() { cout << name << endl; } }; // 子类Student继承Person class Student : public Person { private: int id; public: Student(string n,int i){ name = n; // 可直接访问父类protected成员 id = i; } };特性 3:多态(Polymorphism)
- 含义:同一个接口,不同对象产生不同行为;依托虚函数实现。
- 对比 C 语言:C 语言无多态,只能用函数指针手动模拟,写法复杂。
3. 对象的创建、内存、访问方式
(1)两种创建对象方式
- 栈对象(自动释放,常用)
ExamInfo e("英语",'B');- 堆对象(new 创建,手动 delete 释放)
ExamInfo* p = new ExamInfo("高数",true); delete p;C 语言对比:C 只能malloc/free,无自动构造析构;C++ new 自动调用构造,delete 自动调用析构。
(2)成员访问运算符
.点运算符:栈对象访问e.show();->箭头运算符:对象指针访问p->show();C 语言结构体完全一致,但 C 没有成员函数。
4. 构造函数 & 析构函数(核心基础)
4.1 构造函数 Constructor
- 规则:函数名和类名完全相同,无返回值,对象创建时自动调用,仅执行一次;
- 作用:初始化对象成员,替代 C 语言手动赋值结构体;
- 分类:无参构造、有参构造、重载构造、拷贝构造;
- 对比 C 语言:C 无构造函数,结构体创建后必须手动赋值,容易忘记初始化产生垃圾值。
4.2 析构函数 Destructor
- 规则:
~类名(),无参数无返回值,一个类只能 1 个析构;对象销毁时自动调用; - 作用:释放堆内存、关闭文件、释放资源;
- 对比 C 语言:C 需要手动 free,忘记释放造成内存泄漏。
示例:
class Test { private: char* buf; public: // 构造 Test(){ buf = new char[100]; // 分配堆内存 } // 析构 ~Test(){ delete[] buf; // 自动释放,防止内存泄漏 } };5. 常量对象、常成员函数(细节知识点)
const 类名 对象:常量对象,只能调用常成员函数,不能修改任何成员;- 常成员函数语法:
void func() const {},函数内禁止修改成员变量; - 对比 C 语言:C 无 const 对象区分,常量结构体依然可以通过指针篡改。
第二大部分:构造函数初始化列表 完整详解(含 C 对比、使用场景、细节坑点)
1. 完整语法回顾
类名(形参) : 成员1(值), 成员2(值), 联合体成员(值) { 函数体 }2. 初始化列表 VS 函数体内赋值 VS C 语言结构体初始化
| 对比项 | C 语言结构体初始化 | C++ 构造函数初始化列表 | C++ 构造函数体内赋值 |
|---|---|---|---|
| 执行时机 | 定义结构体时一次性赋值 | 对象创建瞬间直接初始化成员 | 成员先默认构造,再二次覆盖赋值 |
| 底层性能 | 一次拷贝,无额外开销 | 一次构造,性能最优 | 两次构造,存在多余开销 |
| const / 引用支持 | 不支持 | 完美支持 | 直接编译报错 |
| 自定义类成员 | 无法处理无参构造的类 | 直接初始化,无报错 | 编译失败 |
| 无名联合体 | C 语言不支持类内联合体 | 直接初始化联合体成员 | 赋值会覆盖内存,易乱码 |
3. 四种强制必须使用初始化列表场景(核心必记)
- 类内
const常量成员; - 引用类型成员;
- 包含自定义类对象,且该类无无参构造函数;
- 子类构造调用父类构造函数。
示例:const 成员只能用初始化列表
class Demo { private: const int a; // 常量,无法函数内赋值 public: // 正确:初始化列表赋值const Demo(int x):a(x){} // 错误写法:Demo(int x){ a = x; } 编译报错 };4. 高频易错细节梳理
- 初始化顺序和书写顺序无关,严格按照类中成员声明从上到下初始化;
- 不能用后定义成员初始化前定义成员,产生随机垃圾值;
- 初始化列表不属于函数体代码,执行时机早于
{}内部代码。
第三大部分:类内无名联合体 union 完整详解(ExamInfo 案例 + C 对比 + 代码细节 + 故障总结)
1. C 语言联合体 VS C++ 类内无名联合体 核心区别
- C 语言 union:独立数据类型,只能基础类型,必须定义变量才能访问,无法绑定逻辑标记;
- C++ 类内无名 union:嵌入类内部,成员直接作为类成员使用,配合类的枚举、构造函数自动管理;
- 共同特性:共用同一块内存,大小等于最大成员字节,同一时间仅一个数据有效。
2. 核心规则复盘
- 无名联合体语法:类内直接
union { char grade; bool pass; int percent; }; - 访问简化:无需联合体对象,直接
this->grade; - 配套机制:必须搭配枚举
mode记录当前有效成员,否则读取乱码; - 类型限制:联合体不能存放
string、自定义类(带构造析构),只能内置基础类型。
3. 完整可编译案例代码(整合封装、重载构造、初始化列表)
#include <iostream> #include <string> using namespace std; class ExamInfo { private: string courseName; // 课程名 // 枚举标记:记录当前联合体哪个成员有效 enum { GRADE, PASS, PERCENTAGE } mode; // 类内无名联合体 union { char grade; // 等级制 A/B/C bool pass; // 通过/不通过 int percent; // 百分制 0-100 }; public: // 构造1:等级计分,初始化列表同步赋值mode+联合体 ExamInfo(string name, char g) : courseName(name), mode(GRADE), grade(g) {} // 构造2:通过标识计分 ExamInfo(string name, bool p) : courseName(name), mode(PASS), pass(p) {} // 构造3:百分制计分 ExamInfo(string name, int per) : courseName(name), mode(PERCENTAGE), percent(per) {} // 打印成绩函数 void show() { cout << courseName << ": "; switch (mode) { case GRADE: cout << grade << endl; break; case PASS: cout << (pass ? "PASS" : "FAIL") << endl; break; case PERCENTAGE: cout << percent << endl; break; default: cout << "数据无效,乱码!" << endl; } } }; int main() { ExamInfo e1("English", 'B'); ExamInfo e2("Calculus", true); ExamInfo e3("C++Programming", 85); e1.show(); e2.show(); e3.show(); return 0; }输出结果:
plaintext
English: B Calculus: PASS C++Programming: 854. 高频故障、易错细节汇总(自学避坑核心)
- 数据乱码问题:只修改联合体成员,不更新 mode 枚举 → 读取乱码(最高频问题)
ExamInfo e("Math",90); e.percent = 59; // 修改联合体 // 忘记修改mode,show仍然读取百分制没问题;若再赋值grade不更新mode直接乱码- 类型编译限制:联合体存储 string 等复杂类 → 编译报错,联合体仅支持无构造析构基础类型;
- 内存覆盖特性:同时给联合体多个成员赋值,后赋值直接覆盖前一个,数据丢失;
- C 语言原生缺陷:C 无法把 union 封装进结构体并绑定标记,只能手动维护状态,极易出错。
5. 内存占用计算示例
联合体成员:char (1B)、bool (1B)、int (4B) 最大类型 int 占 4 字节 → 联合体总内存 4 字节。 整个 ExamInfo 对象内存:string (系统固定大小) + 枚举 int (4B) + 联合体 (4B)。
第四大部分:核心知识点总汇总(自学复盘、查漏补缺)
一、基础概念核心要点
- C++ 类默认访问权限:private;C++ struct 默认 public;C struct 无访问控制;
- 初始化列表执行顺序:按成员声明顺序,和书写顺序无关;
- 联合体内存大小等于内部最大基础类型;
- const、引用成员必须使用初始化列表初始化;
- 构造函数无返回值,析构函数唯一、无参;
- 无名联合体成员直接属于外部类,无需对象前缀访问。
二、易混淆认知纠正
- ❌ 错误认知:初始化列表书写顺序决定成员初始化顺序 → 正确:按类内成员声明顺序初始化
- ❌ 错误认知:联合体可以同时保存多个有效数据 → 正确:内存覆盖,仅一个数据有效
- ❌ 错误认知:C 语言结构体可以包含成员函数 → 正确:C 语言不支持,仅 C++ 支持
- ✅ 正确认知:类内无名联合体不需要自定义类型名,直接定义使用
- ✅ 正确认知:常量对象只能调用 const 修饰的常成员函数
三、代码编写高频注意事项
- 构造函数体内禁止给 const、引用成员赋值,必须移到初始化列表;
- 修改联合体成员时,必须同步更新 mode 枚举,避免数据乱码;
- 类私有成员禁止外部直接访问,需封装 public get/set 读写接口;
- 子类继承父类时,若父类无无参构造,必须在子类初始化列表调用父类有参构造;
- 联合体中仅可定义基础内置类型,禁止定义 string、自定义类等复杂类型。
补充:C 语言 vs C++ 面向对象 核心总结
C 语言面向过程:数据和操作分离,无保护、无自动初始化、无复用机制; C++ 面向对象:类封装数据与行为,构造 / 析构自动管理资源,联合体、继承、多态扩展能力,严格权限控制,节省内存、代码易维护。