多重继承下的菱形继承

cpp, 中关于继承的一点问题...

单继承

  • 只有一父类的时候,称之为单继承

多继承

  • 一个子类有两个及以上的父类,这个时候,称之为多继承

菱形继承

  • 多继承下面的特殊状态, 会产生一些问题。
    • 当一个类的父类们同时拥有相同的父类的时候,就会发生二义性 菱形继承

问题

1 二义性
二义性
  • 解决访问的二义性相对简单,可以通过添加类的限定从而访问到具体的数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Author :public Student, public Teacher
    {
    public:
    Author()
    {
    Student::name_ = "";
    Teacher::name_ = "";
    }
    };

2 数据冗余
  • 从上面的图来看,菱形继承的数据发生了冗余,以及带来数据访问时候的二义性
    • Author中存在两份Person的数据
  • 数据冗余的问题在CPP中通过使用虚继承解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Person
{
public:
Person()
{
std::cout << __FUNCTION__ << std::endl;
}
Person(std::string name,std::string ID = "")
:name_(name)
,id_(ID)
{
std::cout << __FUNCTION__ << std::endl;
}
~Person()
{
std::cout << __FUNCTION__ << std::endl;
}
protected:
std::string name_{"name"};
std::string birthDay_{"2000.1.1"};
std::string id_{"XXXXXXXXXXXXXXXXXX"};
};

class Student : virtual public Person
{
public:
Student(std::string name_)
:Person("stu")
{
std::cout << __FUNCTION__ << std::endl;
}
~Student() = default;
protected:
std::string stuId_;
};


class Teacher : virtual public Person
{
public:
Teacher(std::string name_)
:Person("teacher")
{
std::cout << __FUNCTION__ << std::endl;
}
~Teacher() = default;
protected:
std::string teacherId_;
};

虚继承的实现原理

  • virtual base table pointer

  • virtual table

  • 我们基于上面部分分析关于虚继承的实现的原理

    • VSclass布局查看工具
    • 上述信息包含: 虚函数表及布局,类的大小,类成员占用大小,成员相较于起始地址的偏移量,字节对其信息
  • 上述布局中我们可以看到,当虚继承发生,就会在虚基类的直接子类中产生一个vbptr指针,这个指针指向一个虚基类表,Author继承自Teacher&Student,同样继承了虚基类指针,同时Author只存在一份Person的数据.

  • 我们通过虚基类表中记录的偏移(虚基类到当前类),就可以访问到虚基类的数据成员.