当前位置: 首页 > news >正文

C++ 面向对象进阶:继承深化与多态详解

上一篇博客我们介绍了 C++ 面向对象的基础概念,包括类与对象、封装、构造 / 析构函数及继承的基本用法。本文将深入探讨继承的高级特性,并详细讲解面向对象的另一核心特性 —— 多态,帮助你理解复杂类层次设计和灵活的接口实现。

一、继承的深入探讨

继承作为代码复用的核心机制,在实际开发中存在诸多细节需要掌握。除了基础的继承语法,我们还需关注成员访问控制、构造函数传递及菱形继承等问题。

1.1 继承中的构造与析构顺序

当子类继承父类时,对象的创建和销毁会涉及父子类构造函数与析构函数的调用顺序,这是内存管理的关键:

  • 构造顺序:先调用父类构造函数,再调用子类构造函数(先有父再有子)
  • 析构顺序:与构造相反,先调用子类析构函数,再调用父类析构函数(先销毁子再销毁父)
#include <iostream>
using namespace std;class Parent {
public:Parent() { cout << "Parent构造函数" << endl; }~Parent() { cout << "Parent析构函数" << endl; }
};class Child : public Parent {
public:Child() { cout << "Child构造函数" << endl; }~Child() { cout << "Child析构函数" << endl; }
};int main() {Child c;// 输出顺序:// Parent构造函数 → Child构造函数// 程序结束时:Child析构函数 → Parent析构函数return 0;
}

注意:若父类没有默认构造函数,子类必须在初始化列表中显式调用父类的有参构造:

class Parent {
public:Parent(int a) { cout << "Parent有参构造:" << a << endl; }
};class Child : public Parent {
public:// 必须通过初始化列表调用父类有参构造Child() : Parent(10) { cout << "Child构造函数" << endl; }
};

1.2 继承中的同名成员处理

当子类与父类存在同名成员时,需要通过作用域分辨符区分:

  • 同名成员变量:子类对象.父类名::成员 访问父类成员
  • 同名成员函数:若子类重写了父类函数,直接调用会执行子类版本;需加作用域访问父类版本
class Parent {
public:int num = 100;void show() { cout << "Parent show: " << num << endl; }
};class Child : public Parent {
public:int num = 200;  // 同名成员变量void show() { cout << "Child show: " << num << endl; }  // 同名成员函数
};int main() {Child c;cout << c.num << endl;  // 200(子类成员)cout << c.Parent::num << endl;  // 100(父类成员)c.show();  // Child show: 200(子类函数)c.Parent::show();  // Parent show: 100(父类函数)return 0;
}

1.3 菱形继承问题与虚继承

当一个子类同时继承两个父类,而这两个父类又继承自同一个基类时,会产生菱形继承问题:

  • 数据冗余:子类会保存两份基类成员
  • 二义性:访问基类成员时无法确定来自哪个父类
// 菱形继承结构
class Base { public: int a; };
class Parent1 : public Base {};
class Parent2 : public Base {};
class Child : public Parent1, public Parent2 {};int main() {Child c;// c.a = 10;  // 错误:二义性(Parent1::a 还是 Parent2::a?)c.Parent1::a = 10;  // 需显式指定,仍存在数据冗余return 0;
}

解决方法:虚继承(virtual)通过virtual关键字修饰父类的继承方式,使基类成员在子类中只保留一份:

class Base { public: int a; };
// 虚继承:Parent1和Parent2共享Base成员
class Parent1 : virtual public Base {};
class Parent2 : virtual public Base {};
class Child : public Parent1, public Parent2 {};int main() {Child c;c.a = 10;  // 正确:仅一份Base::areturn 0;
}

二、多态:面向对象的灵活性核心

多态是指同一接口在不同场景下表现出不同行为,分为静态多态动态多态

  • 静态多态:编译期确定(函数重载、运算符重载)
  • 动态多态:运行期确定(基于虚函数的继承体系)

2.1 动态多态的实现条件

  1. 存在继承关系
  2. 子类重写父类的虚函数(函数名、参数、返回值完全一致)
  3. 父类指针或引用指向子类对象
#include <iostream>
using namespace std;// 父类:定义虚函数
class Animal {
public:// 虚函数:用virtual修饰virtual void speak() {cout << "动物叫" << endl;}
};// 子类:重写虚函数
class Cat : public Animal {
public:// 重写:函数签名与父类虚函数一致void speak() override {  // override关键字可显式标识重写(C++11)cout << "喵喵叫" << endl;}
};class Dog : public Animal {
public:void speak() override {cout << "汪汪叫" << endl;}
};// 统一接口:接收父类指针
void doSpeak(Animal* animal) {animal->speak();  // 运行时根据实际对象类型调用对应函数
}int main() {Cat cat;Dog dog;doSpeak(&cat);  // 输出:喵喵叫doSpeak(&dog);  // 输出:汪汪叫return 0;
}

2.2 虚函数表与多态原理

C++ 通过虚函数表(vtable) 实现动态多态:

  • 含有虚函数的类会生成一个虚函数表,存储虚函数地址
  • 类的每个对象会包含一个虚表指针(vptr),指向该类的虚函数表
  • 子类重写虚函数时,会替换虚表中对应函数的地址
  • 调用虚函数时,通过 vptr 找到虚表,再调用实际函数地址(运行期确定)

2.3 纯虚函数与抽象类

当父类的虚函数无需实现(仅作为接口)时,可声明为纯虚函数,包含纯虚函数的类称为抽象类

  • 纯虚函数语法:virtual 返回类型 函数名(参数) = 0;
  • 抽象类特点:
    • 不能实例化对象
    • 子类必须重写所有纯虚函数才能实例化,否则仍是抽象类
// 抽象类(接口)
class Shape {
public:// 纯虚函数:仅声明,无实现virtual double calculateArea() = 0;virtual double calculatePerimeter() = 0;
};// 子类实现接口
class Circle : public Shape {
private:double radius;
public:Circle(double r) : radius(r) {}// 必须重写所有纯虚函数double calculateArea() override {return 3.14 * radius * radius;}double calculatePerimeter() override {return 2 * 3.14 * radius;}
};int main() {// Shape s;  // 错误:抽象类不能实例化Shape* circle = new Circle(5);cout << "面积:" << circle->calculateArea() << endl;  // 78.5delete circle;return 0;
}

2.4 多态的应用场景

多态是框架设计的核心思想,典型应用包括:

  1. 接口统一:用父类指针 / 引用接收不同子类对象,简化调用
  2. 扩展方便:新增子类无需修改原有接口代码(开闭原则)
  3. 回调机制:通过虚函数实现事件响应的灵活绑定

例如图形绘制框架:

// 框架代码(无需修改)
void drawShapes(Shape* shapes[], int count) {for (int i = 0; i < count; i++) {cout << "面积:" << shapes[i]->calculateArea() << endl;}
}// 扩展新图形(只需新增子类)
class Rectangle : public Shape {// ...实现纯虚函数
};int main() {Shape* shapes[] = {new Circle(5), new Rectangle(3,4)};drawShapes(shapes, 2);  // 统一调用,自动适配不同图形return 0;
}

三、继承与多态的常见问题

3.1 虚析构函数

当父类指针指向子类对象时,若父类析构函数不是虚函数,删除指针只会调用父类析构,导致子类资源泄漏:

class Parent {
public:// 虚析构:确保子类析构被调用virtual ~Parent() { cout << "Parent析构" << endl; }
};class Child : public Parent {
private:int* data;
public:Child() { data = new int; }~Child() { delete data; cout << "Child析构" << endl; }
};int main() {Parent* p = new Child;delete p;  // 若Parent析构非虚函数,仅调用Parent析构// 虚析构时输出:Child析构 → Parent析构return 0;
}

结论:基类析构函数应声明为虚函数。

3.2 不能被重写的函数

  • 静态成员函数:属于类,无 this 指针,无法放入虚函数表
  • 构造函数:对象未完全创建,无法多态调用
  • 友元函数:不是类成员函数,不存在重写概念

四、总结

本文深入讲解了 C++ 继承的高级特性(构造顺序、同名成员、菱形继承)和多态的核心机制(虚函数、纯虚函数、抽象类),主要知识点包括:

  • 继承中构造与析构的调用顺序及参数传递
  • 虚继承解决菱形继承的数据冗余和二义性
  • 动态多态的实现条件与虚函数表原理
  • 纯虚函数与抽象类在接口设计中的应用
  • 虚析构函数的重要性

多态是面向对象编程灵活性的核心,掌握它能让你设计出更具扩展性和复用性的代码。下一篇将介绍运算符重载、模板等 C++ 高级特性,敬请关注!

如果本文对你有帮助,欢迎点赞收藏,有任何疑问或补充欢迎在评论区交流~

http://www.dtcms.com/a/423861.html

相关文章:

  • 达建网站长沙网站快速排名优化
  • 网站浏览器兼容性问题吗产品介绍网站源码
  • 20.Nginx 服务器
  • CTFshow萌新杂项详细解题攻略及学习笔记
  • jsp网站服务器如何做防护飘云网络科技有限公司
  • Effective Python 第34条: 避免使用 `send()` 给生成器注入数据
  • wordpress站内301上海对外经贸大学
  • 当AI助手“记忆混乱”:理解与应对Roo Code的上下文污染问题
  • Docker 网络详解:(二)虚拟网络环境搭建与测试
  • 【Docker】在项目中如何实现Dockerfile 文件编写
  • 专门做任务的网站吗wordpress数据库文件
  • AMD KFD的BO设计分析系列5-3:VM-amdgpu_bo_va_mapping
  • FilterSolutions2019使用指南
  • 方寸控股解读:《工业园区高质量发展指引》下的园区升级路径
  • 学习总结——接口测试基础
  • 好的案例展示网站在线设计平台招募设计师
  • 阳泉网站建设哪家便宜上海哪家公司提供专业的网站建设
  • TCP的理解
  • 鸿蒙应用主题模式切换实现详解
  • Matplotlib `imsave()` 函数详解
  • NFC技术如何破解电子制造领域的效率瓶颈与追溯难题
  • sk06.【scikit-learn基础】--『监督学习』之决策树
  • 银川怎么做网站wordpress炫酷站
  • 网站说明页命名大连响应式网站建设
  • 程序综合实践第二次递归与dfs
  • 半双工 vs 全双工:对讲机与电话的根本区别
  • 华茂达建设集团网站公司注册网上核名业务如何终止
  • 从虚拟背景到绿幕替换:人像分割功能在直播美颜sdk中的落地应用
  • 建设电影网站论文微信营销模式有哪些
  • Spring的配置(使用注解)