本文系统讲解了 C++ 中的继承机制,包括单继承、多继承、菱形继承及虚拟继承,配合图解和代码示例直观展示各类继承的对象模型、内存布局及访问特点,重点说明虚拟继承如何通过共享基类实例、虚基表偏移和构造顺序,解决菱形继承的数据冗余与二义性问题。 继承的认识和基础学习详解----------》请点击
一个派生类只有一个直接基类的时候称这个继承为单继承 Person ↓ Teacher ↓ Student (单链)

// 基类:人
class Person
{
public:
string name;
int age;
void ShowPerson()
{
cout << "姓名: " << name << ",年龄: " << age << endl;
}
};
// 派生类:老师(继承人)
class Teacher : public Person
{
public:
string subject;
void ShowTeacher()
{
cout << "姓名: " << name
<< ",年龄: " << age
<< ",教授科目: " << subject << endl;
}
};
// 派生类:学生(继承老师)
class Student : public Teacher
{
public:
string school;
void ShowStudent()
{
cout << "姓名: " << name
<< ",年龄: " << age
<< ",教授科目: " << subject
<< ",学校: " << school << endl;
}
};
int main()
{
Student s;
s.name = "呆头"; // 来自 Person
s.age = 20; // 来自 Person
s.subject = "C++"; // 来自 Teacher
s.school = "野鸡大学"; // 来自 Student
s.ShowStudent();
return 0;
}运行结果

多链

// 基类:人
class Person
{
public:
string name;
int age;
void ShowPerson()
{
cout << "姓名: " << name << ",年龄: " << age << endl;
}
};
// 派生类:老师
class Teacher : public Person
{
public:
string subject;
void Teach()
{
cout << name << " 正在教授 " << subject << " 课程。" << endl;
}
};
// 派生类:学生
class Student : public Person
{
public:
string school;
void Study()
{
cout << name << " 在 " << school << " 学习。" << endl;
}
};
int main()
{
Teacher t;
t.name = "张老师";
t.age = 35;
t.subject = "C++";
t.ShowPerson();
t.Teach();
cout << "------------------" << endl;
Student s;
s.name = "李华";
s.age = 20;
s.school = "宁夏大学";
s.ShowPerson();
s.Study();
return 0;
}一个派生类有两个或者以上的直接基类时称这个继承关系为多继承,多继承对象在内存中的模型是先继承的基类在前面,后继承的基类在后面,派生类成员放在最后面。 多继承的使用方法是在子类的位置对多个父类使用逗号,进行间隔,其余方式public形式不变,进行继承
Person Teacher Athlete
\ | /
\ | /
----> Student// 基类:人
class Person
{
public:
string name;
int age;
};
// 老师类
class Teacher
{
public:
string subject;
void Teach() { cout << "正在教授 " << subject << " 课程。" << endl; }
};
// 运动员类
class Athlete
{
public:
string sport;
void Train() { cout << "正在训练 " << sport << " 项目。" << endl; }
};
// 学生类(多继承)
class Student : public Person, public Teacher, public Athlete
{
public:
string school;
void Show()
{
cout << "姓名: " << name
<< ",年龄: " << age
<< ",学校: " << school
<< ",教授科目: " << subject
<< ",运动项目: " << sport << endl;
}
};
int main()
{
Student s;
s.name = "李华";
s.age = 20;
s.school = "宁夏大学";
s.subject = "C++";
s.sport = "800米";
s.Show();
s.Teach();
s.Train();
return 0;
}运行结果

菱形继承是多继承的⼀种特殊情况。菱形继承的问题有数据冗余和⼆义性的问题。⽀持多继承就⼀定会有菱形继承,像Java就直接不⽀持多继承,规避掉了这⾥的问题,所以实践中我们也是不建议设计出菱形继承这样的模型的。

#include <iostream>
#include <string>
using namespace std;
// 基类:人
class Person
{
public:
string name;
int age;
};
// 老师继承人
class Teacher : public Person
{
public:
string subject;
void Teach()
{
cout << name << " 正在教授 " << subject << endl;
}
};
// 运动员继承人
class Athlete : public Person
{
public:
string sport;
void Train()
{
cout << name << " 正在训练项目:" << sport << endl;
}
};
// 学生同时继承老师与运动员
class Student : public Teacher, public Athlete
{
public:
string school;
void Show()
{
// ⚠️ 错误:编译器不知道 name 是哪个基类的
// cout << "姓名: " << name << endl; // ❌ 二义性
cout << "Teacher::name = " << Teacher::name << endl;
cout << "Athlete::name = " << Athlete::name << endl;
cout << "学校: " << school << endl;
}
};
int main()
{
Student s;
s.Teacher::name = "张老师";
s.Athlete::name = "李同学"; // 各自一份 name
s.subject = "C++";
s.sport = "800米";
s.school = "宁夏大学";
s.Show();
// 访问需要显式指定路径
s.Teacher::Teach();
s.Athlete::Train();
return 0;
}Student ├── Teacher::Person │ ├── name │ └── age ├── Athlete::Person │ ├── name │ └── age └── school



如何解决菱形继承带来的弊端?——》使用虚拟继承! 以上述对象模型为例,在 Teacher 和 Athlete 继承 Person 时采用虚拟继承,即可避免 Student 同时拥有两份 Person 子对象。
class Person
{
public:
string name;
int age;
};
// 老师虚继承人
class Teacher : virtual public Person
{
public:
string subject;
};
// 运动员虚继承人
class Athlete : virtual public Person
{
public:
string sport;
};
// 学生多继承
class Student : public Teacher, public Athlete
{
public:
string school;
void Show()
{
cout << "姓名: " << name << endl; // ✅ 不再二义性
cout << "学校: " << school << endl;
}
};回顾我们上述示例是这样的继承关系:
Person
/ \
Teacher Athlete
\ /
Student虚拟继承会让共享的基类在内存中只有一份,并且派生类通过 虚基表(vbt/vptr类似) 来找到这份共享的基类。
Student 对象布局(虚继承):
[ Student 部分 ][ Teacher 部分 ][ Athlete 部分 ][ Person(共享) ]注意 Person 只存在一份。
Teacher、Athlete)内部不直接包含 Person 成员,而是 包含一个指向共享 Person 的指针(实际上是偏移量)。Student 被实例化时,最终会在对象末尾放置一份 Person 数据。Person 时,通过偏移量找到唯一的 Person。示意图:
Student 内存:
[ Student fields ]
[ Teacher fields | pointer_to_shared_Person ]
[ Athlete fields | pointer_to_shared_Person ]
[ Shared Person fields ]虚继承通常涉及两个表:
普通虚表:用于多态函数调用。 虚基表(VBT):用于找到虚继承基类的偏移量。
访问虚基类成员时:
this + offset_of_Person_in_Teacher。this + vbt_offset → 找到唯一 Person。举例:
Student s;
s.Teacher::Person::name = "Alice"; // 编译器查找 Teacher 的虚基表 -> 定位到 Student 中唯一的 Person底层逻辑:
this 是 Student 的起始地址 → 查找虚基表中的 Person 偏移量 → 访问共享 Person。
虚继承的构造顺序也不同:
Person)先构造。Teacher、Athlete)。Student)。这保证了 虚基类只构造一次,防止重复初始化。
虚拟继承的本质可以理解为:
换句话说,虚拟继承底层就是:
“中间类不直接存虚基类,而是存一个指向虚基类偏移的指针;派生类实例化时才分配唯一的虚基类存储。”

C++ 继承方式灵活多样,单继承简单直观,多继承可组合功能但可能出现二义性,而菱形继承会导致数据冗余和访问冲突;虚拟继承通过共享基类实例和虚基表机制有效解决菱形继承问题,同时调整构造顺序保证基类唯一初始化,是处理复杂继承关系的关键技术。