思考:菱形虚拟继承和多态

1.实验名称

菱形继承/虚继承和多态的结构

2.实验目的

探寻菱形继承和菱形虚拟继承 且实现多态情况下 类对象的结构 

3.实验环境

Vistual Studio2022 x86  Debug 

4.实验方法

(1)方法1:打印虚函数表

由于VS的编译器会将虚表的末端设置成0 因此我么可以根据这一特性得到虚表的地址

由此去调用虚函数

(2)方法2:内存窗口查看

通过内存窗口  我们可以知道各个类虚表和变量存放的相对位置

5.实验对象和结果分析

(1)非虚拟继承 

我们先来看 方法1得到的结果

根据这个图我们知道不同虚函数地址存放的位置 

同时 由于B类对象和D类对象一模一样

我们猜测B类起始位置和D类起始位置相同

我们再根据方法2 也可以得到新的图

 由这个图我们可以知道

虚表1指针是存在B对象中A对象部分内

虚表2指针是存在C对象的A对象部分内

虚表1里面存了6个虚函数的地址(0000 0000前面有6个地址)

虚表2里面存了4个虚函数的地址 (0000 0000前面有4个地址)

和我方法1打印的结果刚好符合

可以知道  B::a C::a   b  c  d五个变量的相对位置

因此我们可以将其结构图画出来!

 (2)虚拟继承

 我们先来看 方法1得到的结果

根据这个图我们知道不同虚函数地址存放的位置 

同时 由于B类对象和D类对象一模一样

我们猜测B类起始位置和D类起始位置相同

我们再根据方法2 也可以得到新的图

那么在画图过程中我怎么知道 是虚表指针还是虚基表指针呢?

虚表里面存的是地址  虚基表里面存的是偏移量 

 由这个图我们可以知道

不同虚表和虚基表的相对存储位置

B的虚表里面存了4个虚函数的地址(0000 0000前面有4个地址)

C的虚表里面存了2个虚函数的地址 (0000 0000前面有2个地址)

A的虚表里面存了2个虚函数的地址 (0000 0000前面有2个地址)

和我方法1打印的结果刚好符合

可以知道  B::a C::a   b  c  d四个变量的相对位置

这个地方为什么说是四个对象?

因为这个地方是虚继承   B和C的公共部分A被拿出来了!

所有它们本质上是共用一个a  所以本质上  B::a和C::a本质上是同一个!

因此我们可以将其结构图画出来!

<think>首先,用户要求我回答关于“使用继承多态C++开发示例及相关知识”。我需要基于提供的引用内容来构建我的回答。引用来自几个C++学习笔记,涵盖了继承多态的基本概念、语法应用。 关键点从引用中提取: - 引用[1]:讨论了继承的各个方面,如基本语法、继承方式、对象模型、构造析构顺序、同名成员处理、多继承菱形继承;以及多态的基本语法、纯虚函数、抽象类、虚析构函数等。 - 引用[2]:强调了继承的本质是代码复用,提供了一个简单的继承示例(类B继承自类A)。 - 引用[3]:展示了多态的使用,特别是dynamic_cast在运行时类型识别(RTTI)中的应用,用于安全向下转型。 - 引用[4]:解释了虚拟继承如何解决菱形继承问题(如二义性数据冗余)。 我的回答必须: - 使用中文。 - 结构清晰,帮助用户逐步解决问题。 - 遵循系统级指令:行内数学表达式用$...$,独立公式用$$...$$(尽管在这个上下文中可能不太需要),确保LaTeX语法正确。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 生成相关问题(在末尾添加“§§相关问题§§”部分)。 回答结构: 1. **引言**:简要介绍继承多态的概念。 2. **继承部分**:讲解基本语法、继承方式、关键点(如构造/析构顺序、同名成员处理)、多继承菱形继承。 3. **多态部分**:解释基本语法、虚函数、纯虚函数、抽象类,以及动态多态(dynamic_cast)。 4. **示例代码**:提供C++代码示例来演示继承多态的实际应用。结合引用中的例子。 5. **总结**:强调重要性。 6. **相关问题**:生成相关问题。 确保回答真实可靠,基于提供的引用。如果有不确定的部分,避免推测。 生成相关问题: - 基于主题,如继承的优化、多态的应用场景等。 - 类似示例中的问题格式。 现在,规划具体内容: - **继承示例**:使用引用[2]的简单例子。 - **多态示例**:使用引用[3]的dynamic_cast例子。 - **虚拟继承示例**:使用引用[4]解决菱形继承问题。 代码部分:在回答中包含代码块,使用Markdown格式,如```cpp ... ```。 添加引用标识:当引用特定内容时,在末尾添加[^n]。 回答大纲: 1. **介绍继承多态** - 定义继承:代码复用。 - 定义多态:动态绑定。 2. **继承详解** - 基本语法:如`class B : public A` - 继承方式:public, protected, private [^1] - 对象模型:内存布局 - 构造析构顺序:先基类后派生类 - 同名成员处理:作用域解析符 - 静态成员处理 - 多继承菱形继承:问题及虚拟继承解决方案 [^1][^4] 3. **多态详解** - 基本语法:虚函数关键字`virtual` - 纯虚函数抽象类 - 虚析构函数:确保正确析构 [^1] - dynamic_castRTTI [^3] - 案例:如计算器类、饮品制作 [^1] 4. **综合示例** - 提供一个简单完整的C++程序,演示继承多态。 - 例如:基于引用[3],扩展一个类层次。 5. **总结** - 益处:提高代码可维护性、扩展性。 6. **相关问题** - 例如: 1. 如何处理继承中的资源管理? 2. 多态在游戏开发中的应用? 3. 如何避免多继承的复杂性? 确保代码正确:我将使用简单的示例,类似引用中的。例如,对于继承: ```cpp class Animal { public: void eat() { /*...*/ } }; class Dog : public Animal { public: void bark() { /*...*/ } }; ``` 对于多态: ```cpp class Shape { public: virtual void draw() = 0; }; class Circle : public Shape { public: void draw() override { /*...*/ } }; ``` 结合dynamic_cast例子。 现在,用中文撰写回答。 </think>### C++继承多态开发指南 在C++中,**继承(Inheritance)** 的核心是代码复用,允许创建新类(派生类)基于现有类(基类);**多态(Polymorphism)** 则通过虚函数实现动态绑定,使同一接口在不同对象上产生不同行为[^1][^2]。下面我将逐步解析关键概念、示例开发实践。 --- ### 一、继承详解与开发示例 继承通过语法 `class Derived : access-specifier Base` 实现,其中 `access-specifier` 控制成员访问权限(public、protected 或 private)[^2]。 #### 1. 基本语法与成员访问 ```cpp #include <iostream> using namespace std; class Animal { // 基类 public: void eat() { cout << "Animal eats" << endl; } protected: int age; // 派生类可访问 private: int id; // 仅基类可访问 }; class Dog : public Animal { // public继承 public: void bark() { eat(); // 调用基类方法(public) age = 5; // 访问protected成员 // id = 1; // 错误:private成员不可访问 cout << "Dog barks" << endl; } }; int main() { Dog myDog; myDog.bark(); // 输出:Animal eats\nDog barks // myDog.age = 0; // 错误:protected成员外部不可访问 return 0; } ``` - **访问权限**:public继承时,基类public成员在派生类中仍为public,protected保持protected,private不可访问[^1][^2]。 - **对象模型**:派生类对象包含基类子对象,内存布局优先存储基类数据[^1]。 #### 2. 构造与析构顺序 - **顺序规则**:构造时先基类后派生类;析构时顺序相反(先派生类后基类)[^1]。 ```cpp class Base { public: Base() { cout << "Base constructed" << endl; } ~Base() { cout << "Base destroyed" << endl; } }; class Derived : public Base { public: Derived() { cout << "Derived constructed" << endl; } ~Derived() { cout << "Derived destroyed" << endl; } }; int main() { Derived d; // 输出:Base constructed → Derived constructed → Derived destroyed → Base destroyed return 0; } ``` #### 3. 同名成员与静态成员处理 - **同名成员**:使用作用域解析符 `Base::member` 区分基类与派生类重名成员[^1]。 - **静态成员**:静态变量在基类派生类中共享唯一实例,通过类名直接访问[^1]。 #### 4. 多继承菱形继承问题 - **多继承语法**:`class Derived : public Base1, public Base2`[^1]。 - **菱形继承问题**:当派生类通过多条路径继承同一基类时,引发二义性数据冗余(如 `D` 继承 `B` `C`,两者均继承 `A`)[^4]。 - **虚拟继承解决方案**:使用 `virtual` 关键字确保共享基类单一实例[^4]。 ```cpp class Person { public: string name; }; class Student : virtual public Person { // 虚拟继承 protected: int studentID; }; class Teacher : virtual public Person { // 虚拟继承 protected: int teacherID; }; class TA : public Student, public Teacher { // 解决二义性 public: void setName(string n) { name = n; } // 无二义性 }; ``` 虚拟继承通过偏移指针表实现基类数据共享,避免冗余存储[^4]。 --- ### 二、多态详解与开发示例 多态依赖于 **虚函数(virtual function)** ,实现运行时动态绑定[^3]。 #### 1. 基本语法与纯虚函数 - **虚函数**:使用 `virtual` 关键字声明,派生类可重写(override)。 - **纯虚函数**:定义抽象类(不可实例化),语法 `virtual void func() = 0;`[^1][^3]。 ```cpp class Shape { // 抽象基类 public: virtual void draw() = 0; // 纯虚函数 virtual ~Shape() {} // 虚析构(关键!) }; class Circle : public Shape { public: void draw() override { // 重写虚函数 cout << "Draw Circle" << endl; } }; class Square : public Shape { public: void draw() override { cout << "Draw Square" << endl; } }; int main() { Shape* shapes[] = {new Circle(), new Square()}; for (Shape* s : shapes) { s->draw(); // 多态调用:输出 Draw Circle → Draw Square delete s; // 虚析构确保正确释放派生对象 } return 0; } ``` #### 2. `dynamic_cast` 与 RTTI - **作用**:安全向下转型(downcasting),需基类有虚函数(启用RTTI)[^3]。 - **示例**:检查对象类型并调用派生类特有方法。 ```cpp class Base { public: virtual void func() { cout << "Base::func()" << endl; } }; class Derived : public Base { public: void func() override { cout << "Derived::func()" << endl; } void special() { cout << "Derived special method" << endl; } }; void process(Base* ptr) { Derived* d = dynamic_cast<Derived*>(ptr); // 安全转型 if (d) { d->special(); // 调用派生类方法 } else { ptr->func(); // 默认行为 } } int main() { Base b; Derived d; process(&b); // 输出:Base::func() process(&d); // 输出:Derived special method return 0; } ``` #### 3. 虚析构与纯虚析构 - **必要性**:基类指针删除派生对象时,虚析构(`virtual ~Base() {}`)确保调用完整析构链[^1]。纯虚析构(`virtual ~Base() = 0;`)需提供实现(`Base::~Base() {}`)以避免链接错误[^1]。这在资源管理(如文件句柄)中至关重要。 --- ### 三、开发实践建议 1. **优先使用组合而非多继承**:多继承增加复杂性,虚拟继承仅用于菱形问题[^4]。 2. **虚函数开销**:虚函数通过虚表(vtable)实现,有轻微性能开销,避免在性能关键路径滥用。 3. **案例应用**: - **计算器类**:基类定义接口 `virtual double calculate() = 0;`,派生类实现不同运算[^1]。 - **游戏开发**:`Entity` 基类含 `virtual void update()`,派生类(如 `Player`、`Enemy`)定制行为。 多态在框架设计(如GUI事件处理)中提升扩展性——新增派生类无需修改基类逻辑[^3][^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值