深入浅出 C++ 面向对象编程:读《C++语言程序设计教程》核心思想与实战

📅 2026/6/25 13:04:28
深入浅出 C++ 面向对象编程:读《C++语言程序设计教程》核心思想与实战
配套学习《C语言程序设计教程》第五章 ~ 第八章适合人群已掌握 C 基础语法想系统学习 OOP 的同学阅读时长约 18 分钟一、前言如果说 C 语言是面向过程的菜刀那 C 就是在菜刀基础上给你配了一套完整的厨房。面向对象编程OOP 正是 C 区别于 C 的灵魂所在。本文围绕《C语言程序设计教程》核心章节把类与对象、封装、继承、多态四大支柱用通俗例子 完整代码讲透建议先看我的第一篇基础篇再读本文。掌握 OOP 之后你才真正进入 C 的大门。二、目录面向对象的思想起源类与对象从图纸到实物封装用访问权限保护数据构造函数与析构函数继承代码复用的艺术多态同一种行为的不同表现综合案例学生成绩管理系统学习建议与避坑指南三、面向对象的思想起源面向过程C 关注的是怎么做先做哪一步后做哪一步。面向对象C 关注的是谁来做把数据和操作数据的方法打包成一个整体。举个例子把大象装进冰箱面向过程写法打开冰箱门把大象塞进去关上冰箱门面向对象写法冰箱 类有 开门()、关门() 方法大象 类有 进入(冰箱) 方法主程序冰箱.开门(); 大象.进入(冰箱); 冰箱.关门();后者更贴近现实世界的思维方式也更容易扩展比如换成把长颈鹿装进冰箱只需要新增一个 长颈鹿 类。四、类与对象从图纸到实物类Class 是抽象的模板对象Object 是类的具体实例。cpp#include#includeusing namespace std;// 定义一个学生类class Student {public: // 公有成员外部可访问string name; // 姓名int age; // 年龄int score; // 分数void introduce() { // 成员函数 cout 我叫 name 今年 age 岁 考了 score 分 endl; }};int main() {Student stu1; // 创建对象实例化stu1.name “张三”;stu1.age 18;stu1.score 95;stu1.introduce();Student stu2; // 再创建一个对象 stu2.name 李四; stu2.age 19; stu2.score 88; stu2.introduce(); return 0;} 类就像学生信息登记表模板对象就是填写好的具体某位同学。五、封装用访问权限保护数据直接暴露数据很容易被误改封装 提供了三种访问权限表格关键字 权限 使用场景public 公有 外部接口成员函数private 私有 内部数据成员变量默认权限protected 保护 给子类访问外部不能访问cppclass Account {private: // 私有外部不能直接访问string owner;double balance;public: // 公有接口void setOwner(string name) {if (name.empty()) {cout “姓名不能为空” endl;return;}owner name;}void deposit(double money) { if (money 0) { cout 存款金额必须为正 endl; return; } balance money; } void show() { cout owner 的余额: balance 元 endl; }};int main() {Account acc;acc.setOwner(“王五”);acc.deposit(1000);acc.deposit(-500); // 不会执行余额不变acc.show();// acc.balance 99999; // ❌ 编译错误私有成员不能直接访问return 0;}封装的核心思想把数据藏起来对外只暴露做什么方法不暴露怎么做细节。这样数据更安全修改内部实现也不会影响外部调用。六、构造函数与析构函数6.1 构造函数对象出生时自动调用cppclass Person {private:string name;int age;public:// 默认构造函数无参Person() {name “未命名”;age 0;cout “默认构造被调用” endl;}// 带参构造函数 Person(string n, int a) { name n; age a; cout 带参构造被调用 endl; } // 拷贝构造函数 Person(const Person p) { name p.name; age p.age; cout 拷贝构造被调用 endl; } void show() { cout name age endl; }};int main() {Person p1; // 调用默认构造Person p2(“张三”, 18); // 调用带参构造Person p3 p2; // 调用拷贝构造p1.show(); // 未命名 0p2.show(); // 张三 18p3.show(); // 张三 18return 0;}6.2 析构函数对象销毁前自动调用cppclass Array {private:int *data;int size;public:Array(int n) {size n;data new int[n]; // 申请堆内存cout “申请了 n 个 int 的空间” endl;}~Array() { delete[] data; // 释放堆内存防止内存泄漏 cout 释放了 size 个 int 的空间 endl; } void set(int i, int v) { if (i 0 i size) data[i] v; } int get(int i) { return data[i]; }};⚠️ 必考点构造函数和析构函数成对出现。有 new 就要有 delete否则会造成内存泄漏这是 C 程序员最容易犯的错误之一。七、继承代码复用的艺术继承 让子类直接拥有父类的成员同时可以扩展自己的新功能。cpp// 父类基类class Animal {protected: // protected 让子类能访问string name;int age;public:Animal(string n, int a) : name(n), age(a) {}void eat() { cout name 在吃东西 endl; } void sleep() { cout name 在睡觉 endl; }};// 子类派生类class Dog : public Animal { // 公有继承private:string breed; // 品种public:Dog(string n, int a, string b) : Animal(n, a), breed(b) {}void bark() { // 子类自己的方法 cout name 在汪汪叫 endl; } void showInfo() { cout 名字: name 年龄: age 品种: breed endl; }};int main() {Dog dog(“旺财”, 3, “柴犬”);dog.eat(); // 继承自父类dog.sleep(); // 继承自父类dog.bark(); // 子类自己的方法dog.showInfo();return 0;}三种继承方式对比表格继承方式 父类 public 成员 父类 protected 成员 父类 private 成员public 继承 仍是 public 仍是 protected 不可访问protected 继承 变成 protected 仍是 protected 不可访问private 继承 变成 private 变成 private 不可访问 实战建议99% 的情况下用 public 继承即可其他两种方式会让代码关系复杂化不建议初学者使用。八、多态同一种行为的不同表现多态 指的是父类指针/引用调用同一个方法实际执行的是子类的方法。实现多态的两个关键条件继承 虚函数父类方法前加 virtual 关键字通过父类指针或引用调用cpp#includeusing namespace std;class Shape {public:virtual double area() { // 虚函数return 0;}virtual ~Shape() {} // 虚析构保证子类对象能被正确销毁};class Circle : public Shape {private:double r;public:Circle(double radius) : r(radius) {}double area() { // 重写父类的虚函数return 3.14159 * r * r;}};class Rectangle : public Shape {private:double w, h;public:Rectangle(double width, double height) : w(width), h(height) {}double area() {return w * h;}};int main() {Shape *p1 new Circle(5);Shape *p2 new Rectangle(4, 6);cout 圆形面积: p1-area() endl; // 调用 Circle::area cout 矩形面积: p2-area() endl; // 调用 Rectangle::area delete p1; delete p2; return 0;}运行结果plaintext圆形面积: 78.5398矩形面积: 24如果父类的 area() 不加 virtual那么无论指针指向什么子类调用的都是 Shape::area()输出两个 0。这就是静态绑定和动态绑定的区别。⚠️ 面试常考题构造函数能不能是虚函数不能。析构函数要不要写成虚函数有继承时一定要否则子类对象可能释放不完整。九、综合案例学生成绩管理系统把上面学的类 继承 多态组合起来cpp#include#include#includeusing namespace std;// 抽象基类class Person {protected:string name;int id;public:Person(string n, int i) : name(n), id(i) {}virtual void showInfo() 0; // 纯虚函数Person 是抽象类virtual ~Person() {}};// 学生类class Student : public Person {private:double score;public:Student(string n, int i, double s) : Person(n, i), score(s) {}void showInfo() override { cout 学生 - 学号: id 姓名: name 成绩: score endl; } double getScore() const { return score; }};// 教师类class Teacher : public Person {private:string title;public:Teacher(string n, int i, string t) : Person(n, i), title(t) {}void showInfo() override { cout 教师 - 工号: id 姓名: name 职称: title endl; }};int main() {vectorPerson* people; // 用父类指针数组统一管理people.push_back(new Student(张三, 1001, 92.5)); people.push_back(new Student(李四, 1002, 85.0)); people.push_back(new Teacher(王老师, 2001, 副教授)); // 多态调用同一个 showInfo 表现不同 for (auto p : people) { p-showInfo(); } // 释放内存 for (auto p : people) delete p; return 0;}运行结果plaintext学生 - 学号: 1001姓名: 张三成绩: 92.5学生 - 学号: 1002姓名: 李四成绩: 85教师 - 工号: 2001姓名: 王老师职称: 副教授十、学习建议与避坑指南学习路线建议先把基础语法变量、循环、函数写熟练再学类与对象、构造函数然后是继承、访问控制最后啃多态、虚函数、纯虚函数学完基础再看模板STL和异常处理常见坑表格坑 解决方案忘记写虚析构函数 有继承关系时析构函数必加 virtual构造函数中调用虚函数 不要这么做子类的构造还没完成重写虚函数忘记加 override 加上 override 关键字让编译器帮你检查浅拷贝导致 double free 涉及指针成员时手写拷贝构造函数和 operator抽象类想直接实例化 抽象类含纯虚函数不能创建对象只能作为指针类型面试高频题面向对象的三大特性是什么封装、继承、多态多态的实现原理虚函数表vtable 虚表指针vptr重载overload和重写override的区别重载在同一作用域重写在父子类之间写在最后《C语言程序设计教程》这本书的精髓就是用 C 的语法写 Java 一样优雅的面向对象程序。当你理解了类就是图纸、对象就是实物、多态就是同一句话不同人听出不同意思之后C 在你眼中就不再可怕了。下一篇文章我会写 C 模板与 STL 实战包括 vector、map、算法的实际应用敬请期待 推荐练习定义一个 Vehicle 父类派生出 Car、Bike 子类实现 run() 多态写一个 Shape 抽象类提供纯虚函数 area() 和 perimeter()派生 Triangle实现一个简单的 DynamicArray 类包含构造、拷贝构造、析构、push_back如果本文对你有帮助欢迎 点赞 收藏 ⭐ 关注 ➕评论区留下你想看的内容本文基于《C语言程序设计教程》整理总结所有代码均经过编译运行验证。