目录一、 继承不仅是代码的复用1.1 三种继承方式的差异1.2 构造与析构的顺序避坑指南二、 多态让程序具备“生命力”2.1 虚函数Virtual Function2.2 核心代码示例三、 深度思考你必须知道的底层细节3.1 为什么析构函数必须是 virtual3.2 虚函数表的开销四、 总结前言在 C 的面向对象编程OOP中继承和多态是绕不开的两大核心。很多初学者能背出定义但在实际项目中往往会因为忽视了虚析构函数、内存布局等细节导致难以排查的 Bug。今天这篇文章我们就把这两块硬骨头彻底嚼碎。一、 继承不仅是代码的复用继承Inheritance的核心在于Is-a的关系。它允许我们基于已有的类创建新类从而实现逻辑的分层。1.1 三种继承方式的差异在 C 中public、protected和private继承的选择直接决定了子类对外的“成色”Public (最常用)基类的公有成员在派生类中保持公有。Protected基类的公有成员在派生类中变为保护成员适合只给“家里人”用不给外部访问。Private基类的一切在派生类中都变成私有这通常用于“组合”关系的另一种实现。1.2 构造与析构的顺序避坑指南记住一句话先有父后有子先拆子后拆父。在继承体系中构造函数的执行顺序是基类先于派生类而析构函数则是完全反过来的。继承方式基类 Public 成员基类 Protected 成员基类 Private 成员public 继承仍为 public仍为 protected不可见protected 继承 受保护的继承变为 protected变为 protected不可见private 继承变为 private变为 private不可见class Base { public: Base() { cout Base Constructor endl; } virtual ~Base() { cout Base Destructor endl; } // 必须是虚析构 }; class Derived : public Base { public: Derived() { cout Derived Constructor endl; } ~Derived() { cout Derived Destructor endl; } };二、 多态让程序具备“生命力”如果说继承是静态的结构那么多态Polymorphism就是动态的灵魂。它允许我们使用基类的指针或引用来调用派生类的实现。2.1 虚函数Virtual Function多态的基础是virtual关键字。当编译器看到虚函数时它不再进行静态绑定而是通过虚函数表vtable进行动态寻址。2.2 核心代码示例下面是一个典型的多态应用场景#include iostream using namespace std; class Shape { public: // 纯虚函数定义接口规范 virtual void draw() 0; virtual ~Shape() {} }; class Circle : public Shape { public: void draw() override { // 使用 override 明确标识重写 cout 绘制一个圆形 endl; } }; class Rect : public Shape { public: void draw() override { cout 绘制一个矩形 endl; } }; void render(Shape* s) { s-draw(); // 运行时动态决定调用哪个版本 } int main() { Shape* s1 new Circle(); Shape* s2 new Rect(); render(s1); render(s2); delete s1; delete s2; return 0; }三、 深度思考你必须知道的底层细节3.1 为什么析构函数必须是 virtual这是 CSDN 面试题中的高频考点。如果基类的析构函数不是虚函数当你通过Base* ptr new Derived();释放内存时只会调用 Base 的析构函数而 Derived 类的资源如在构造函数中申请的堆内存将无法释放从而导致内存泄漏。3.2 虚函数表的开销多态并非免费的午餐。每个含有虚函数的类都有一个虚函数表每个对象实例都会多出一个指向该表的指针vptr。在嵌入式或对内存极其敏感的开发中需要权衡多态带来的灵活性与额外的内存开销。四、 总结继承是为了建立层级结构和实现代码复用。多态是为了解耦让调用者只需关注接口而非具体实现。Override在子类重写虚函数时务必加上override关键字让编译器帮你检查函数签名是否一致。写在最后C 的强大和复杂在于它给了开发者直接控制内存的能力。理解了继承和多态的底层机制你才能开发时游刃有余。