首先我们已经知道了多继承和单继承的对象模型
接下来我们来看一个复杂的菱形继承
菱形继承的多态继承
#define
_CRT_SECURE_NO_WARNINGS
1
#include
<stdio.h>
#include
<iostream>
using
namespace
std;
class
A
{
public
:
virtual
void
func1()
{
printf(
"A::func1"
);
}
public
:
int
_a;
};
class
B
:
public
A
{
public
:
virtual
void
func1()
{
printf(
"B::func1"
);
}
public
:
int
_b;
};
class
C
:
public
A
{
public
:
virtual
void
func1()
{
printf(
"C::func1"
);
}
public
:
int
_c;
};
class
D
:
public
B
,
public
C
{
public
:
virtual
void
func1()
{
printf(
"D::func1"
);
}
public
:
int
_d;
};
int
main()
{
D
d;
//B
d.
B
::_a = 2;
d._b = 3;
//C
d.
C
::_a = 4;
d._c = 3;
//D
d._d = 4;
printf(
"%d"
,
sizeof
(d));
system(
"pause"
);
return
0;
}

然偶我们再将虚表打出来看一看这两个虚表指针的里保存的虚函数各是什么

我们可以明显的看到,B里的虚表和C里的虚表此时都被D继承了之后进行了虚函数的重写,因为D继承了B和C所以都被D重写了虚函数此时就打出来的是D的函数
菱形虚继承的多态模型
class
A
{
public
:
virtual
void
func1()
{
printf(
"A::func1\n"
);
}
public
:
int
_a;
};
class
B :
virtual
public
A
{
public
:
virtual
void
func1()
{
printf(
"B::func1\n"
);
}
public
:
int
_b;
};
class
C :
virtual
public
A
{
public
:
virtual
void
func1()
{
printf(
"C::func1\n"
);
}
public
:
int
_c;
};
class
D :
public
B,
public
C
{
public
:
virtual
void
func1()
{
printf(
"D::func1\n"
);
}
public
:
int
_d;
};
int
main()
{
D d;
//B
d.B::_a = 2;
d._b = 3;
//C
d.C::_a = 4;
d._c = 3;
//D
d._d = 4;
//D的虚表
PrintVTable((int *)*((int *)&d + (sizeof(D)- sizeof(A))/4));
system(
"pause"
);
return
0;
}
typedef
void
(*FUNC) ();
void
PrintVTable(
int
*VTable)
{
printf(
"虚表地址:%p\n"
, VTable);
int
i = 0;
for
(; VTable[i] != NULL; i++)
{
printf(
"第%d个虚函数的地址:%p------>"
, i, VTable[i]);
FUNC f = (FUNC)VTable[i];
f();
}
printf(
"\n"
);
}
我们可以看到在D继承了B和C之后,首先B和C继承了A的虚表,同时对A进行了虚函数的重写,但是当有两个虚函数同时对A的虚函数进行重写的时候,此时我们其实是不知道A到底是被B还是C重写的,所以当D继承了B和C之后也对这个虚函数进行了重写,最后其实最后D对A的虚函数进行了重写
打印虚表

模型

我们再来看一种情况
当子类里不只有重写的虚函数,还有他自己的没有被重写的虚函数这会是怎样的呢?
1.我们先在B里加一个虚函数
class
B
:
virtual
public
A
{
public
:
virtual
void
func1()
{
printf(
"B::func1\n"
);
}
virtual
void
func2()
{
printf(
"B::func2\n"
);
}
public
:
int
_b;
};
然后我们再来看内存结构,我们会发现D多了4个字节,通过看内存可以发现,我们的B结构里多了一个地址
,我们能确定的是这两个地址里肯定有一个是指向虚基表,那么另一个其实就是虚表啦
我们在徐继承的时候其实B和C都会继承A的虚表它不会自己去在浪费内存再开一个虚表,但是此前提是他们两个都没有多余的虚函数,一旦当他们两个任意一个有了多余的虚函数,那么此时他们自己就会在生成一个虚表,将自己的虚函数放进去,谁有谁开,两个都有的话两个都开

然后在两个同时加虚函数
class
B
:
virtual
public
A
{
public
:
virtual
void
func1()
{
printf(
"B::func1\n"
);
}
virtual
void
func2()
{
printf(
"B::func2\n"
);
}
public
:
int
_b;
};
class
C
:
virtual
public
A
{
public
:
virtual
void
func1()
{
printf(
"C::func1\n"
);
}
virtual
void
func3()
{
printf(
"C::func3\n"
);
}
public
:
int
_c;
};

虚基表
接下来我们再来看一下虚基表,我们之前发现虚基表里其实存的是一个偏移量,那我们在把内存里的虚基表看一下
其实这两种虚表是编译器中最常见的两种虚表,第一行是判断此类有无虚表,第二行很明显是虚继承偏移量
