1)单一的一般继承(带成员变量、虚函数、虚函数覆盖)
2)单一的虚拟继承(带成员变量、虚函数、虚函数覆盖)
3)多重继承(带成员变量、虚函数、虚函数覆盖)
4)重复多重继承(带成员变量、虚函数、虚函数覆盖)
5)钻石型的虚拟多重继承(带成员变量、虚函数、虚函数覆盖)
单一的一般继承
下面,我们假设有如下所示的一个继承关系:
请注意,在这个继承关系中,父类,子类,孙子类都有自己的一个成员变量。而了类覆盖了父类的f()方法,孙子类覆盖了子类的g_child()及其超类的f()。
我们的源程序如下所示:
class
Parent {
public
:
int
iparent;
Parent ():iparent (
10
) {}
virtual
void
f() { cout <<
"
Parent::f()"
<< endl; }
virtual
void
g() { cout <<
"
Parent::g()"
<< endl; }
virtual
void
h() { cout <<
"
Parent::h()"
<< endl; }
};
class
Child :
public
Parent {
public
:
int
ichild;
Child():ichild(
100
) {}
virtual
void
f() { cout <<
"Child::f()"
<< endl; }
virtual
void
g_child() { cout <<
"Child::g_child()"
<< endl; }
virtual
void
h_child() { cout <<
"Child::h_child()"
<< endl; }
};
class
GrandChild :
public
Child{
public
:
int
igrandchild;
GrandChild():igrandchild(
1000
) {}
virtual
void
f() { cout <<
"GrandChild::f()"
<< endl; }
virtual
void
g_child() { cout <<
"GrandChild::g_child()"
<< endl; }
virtual
void
h_grandchild() { cout <<
"GrandChild::h_grandchild()"
<< endl; }
};
我们使用以下程序作为测试程序:(下面程序中,我使用了一个int** pVtab 来作为遍历对象内存布局的指针,这样,我就可以方便地像使用数组一样来遍历所有的成员包括其虚函数表了,在后面的程序中,我也是用这样的方法的,请不必感到奇怪,)
typedef
void
(*Fun)(
void
);
GrandChild gc;
int
** pVtab = (
int
**)&gc;
cout <<
"[0] GrandChild::_vptr->"
<< endl;
for
(
int
i=
0
; (Fun)pVtab[
0
][i]!=NULL; i++){
pFun = (Fun)pVtab[
0
][i];
cout <<
" ["
<<i<<
"] "
;
pFun();
}
cout <<
"[1] Parent.iparent = "
<< (
int
)pVtab[
1
] << endl;
cout <<
"[2] Child.ichild = "
<< (
int
)pVtab[
2
] << endl;
cout <<
"[3] GrandChild.igrandchild = "
<< (
int
)pVtab[
3
] << endl;
其运行结果如下所示:(在VC++ 2003和G++ 3.4.4下)
[0] GrandChild::_vptr-> [0] GrandChild::f() [1] Parent::g() [2] Parent::h() [3] GrandChild::g_child() [4] Child::h1() [5] GrandChild::h_grandchild() [1] Parent.iparent = 10 [2] Child.ichild = 100 [3] GrandChild.igrandchild = 1000
使用图片表示如下:
可见以下几个方面:
1)虚函数表在最前面的位置。
2)成员变量根据其继承和声明顺序依次放在后面。
3)在单一的继承中,被overwrite的虚函数在虚函数表中得到了更新。
多重继承
下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。注意:子类只overwrite了父类的f()函数,而还有一个是自己的函数(我们这样做的目的是为了用g1()作为一个标记来标明子类的虚函数表)。而且每个类中都有一个自己的成员变量:
我们的类继承的源代码如下所示:父类的成员初始为10,20,30,子类的为100
class
Base1 {
public
:
int
ibase1;
Base1():ibase1(
10
) {}
virtual
void
f() { cout <<
"Base1::f()"
<< endl; }
virtual
void
g() { cout <<
"Base1::g()"
<< endl; }
virtual
void
h() { cout <<
"Base1::h()"
<< endl; }
};
class
Base2 {
public
:
int
ibase2;
Base2():ibase2(
20
) {}
virtual
void
f() { cout <<
"Base2::f()"
<< endl; }
virtual
void
g() { cout <<
"Base2::g()"
<< endl; }
virtual
void
h() { cout <<
"Base2::h()"
<< endl; }
};
class
Base3 {
public
:
int
ibase3;
Base3():ibase3(
30
) {}
virtual
void
f() { cout <<
"Base3::f()"
<< endl; }
virtual
void
g() { cout <<
"Base3::g()"
<< endl; }
virtual
void
h() { cout <<
"Base3::h()"
<< endl; }
};
class
Derive :
public
Base1,
public
Base2,
public
Base3 {
public
:
int
iderive;
Derive():iderive(
100
) {}
virtual
void
f() { cout <<
"Derive::f()"
<< endl; }
virtual
void
g1() { cout <<
"Derive::g1()"
<< endl; }
};
我们通过下面的程序来查看子类实例的内存布局:下面程序中,注意我使用了一个s变量,其中用到了sizof(Base)来找下一个类的偏移量。(因为我声明的是int成员,所以是4个字节,所以没有对齐问题。关于内存的对齐问题,大家可以自行试验,我在这里就不多说了)
typedef
void
(*Fun)(
void
);
Derive d;
int
** pVtab = (
int
**)&d;
cout <<
"[0] Base1::_vptr->"
<< endl;
pFun = (Fun)pVtab[
0
][
0
];
cout <<
" [0] "
;
pFun();
pFun = (Fun)pVtab[
0
][
1
];
cout <<
" [1] "
;pFun();
pFun = (Fun)pVtab[
0
][
2
];
cout <<
" [2] "
;pFun();
pFun = (Fun)pVtab[
0
][
3
];
cout <<
" [3] "
; pFun();
pFun = (Fun)pVtab[
0
][
4
];
cout <<
" [4] "
; cout<<pFun<<endl;
cout <<
"[1] Base1.ibase1 = "
<< (
int
)pVtab[
1
] << endl;
** int
s =
sizeof
(Base1)/
4
;**
cout <<
"["
<< s <<
"] Base2::_vptr->"
<<endl;
pFun = (Fun)pVtab[s][
0
];
cout <<
" [0] "
; pFun();
Fun = (Fun)pVtab[s][
1
];
cout <<
" [1] "
; pFun();
pFun = (Fun)pVtab[s][
2
];
cout <<
" [2] "
; pFun();
pFun = (Fun)pVtab[s][
3
];
out <<
" [3] "
;
cout<<pFun<<endl;
cout <<
"["
<< s+
1
<<
"] Base2.ibase2 = "
<< (
int
)pVtab[s+
1
] << endl;
**s = s +
sizeof
(Base2)/
4
;
**
cout <<
"["
<< s <<
"] Base3::_vptr->"
<<endl;
pFun = (Fun)pVtab[s][
0
];
cout <<
" [0] "
; pFun();
pFun = (Fun)pVtab[s][
1
];
cout <<
" [1] "
; pFun();
pFun = (Fun)pVtab[s][
2
];
cout <<
" [2] "
; pFun();
pFun = (Fun)pVtab[s][
3
];
cout <<
" [3] "
;
cout<<pFun<<endl;
s++;
cout <<
"["
<< s <<
"] Base3.ibase3 = "
<< (
int
)pVtab[s] << endl;
s++;
cout <<
"["
<< s <<
"] Derive.iderive = "
<< (
int
)pVtab[s] << endl;
其运行结果如下所示:(在VC++ 2003和G++ 3.4.4下)
[0] Base1::_vptr-> [0] Derive::f() [1] Base1::g() [2] Base1::h() [3] Driver::g1() [4] 00000000 ç 注意:在GCC下,这里是1 [1] Base1.ibase1 = 10 [2] Base2::_vptr-> [0] Derive::f() [1] Base2::g() [2] Base2::h() [3] 00000000 ç 注意:在GCC下,这里是1 [3] Base2.ibase2 = 20 [4] Base3::_vptr-> [0] Derive::f() [1] Base3::g() [2] Base3::h() [3] 00000000 [5] Base3.ibase3 = 30 [6] Derive.iderive = 100
使用图片表示是下面这个样子:
我们可以看到:
1)
每个父类都有自己的虚表。
2)
子类的成员函数被放到了第一个父类的表中。
3)
内存布局中,其父类布局依次按声明顺序排列。
4)
每个父类的虚表中的f()函数都被overwrite成了子类的
f()
。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。