原创作品,允许转载,转载时请务必以超链接形式标明文章
原始出处 、作者信息和本声明。否则将追究法律责任。
http://haoel.blog.51cto.com/313033/124561
重复继承
函数。
字节),而且还有自己的虚函数,自己overwrite覆盖了超类的函数,f1() 为自己的虚函数。
class
B
{
public
:
int
ib;
char
cb;
public
:
B():ib(
0
),cb(
'B'
) {}
virtual
void
f() { cout <<
"B::f()"
<< endl;}
virtual
void
Bf() { cout <<
"B::Bf()"
<< endl;}
};
class
B1 :
public
B
{
public
:
int
ib1;
char
cb1;
public
:
B1():ib1(
11
),cb1(
'1'
) {}
virtual
void
f() { cout <<
"B1::f()"
<< endl;}
virtual
void
f1() { cout <<
"B1::f1()"
<< endl;}
virtual
void
Bf1() { cout <<
"B1::Bf1()"
<< endl;}
};
class
B2:
public
B
{
public
:
int
ib2;
char
cb2;
public
:
B2():ib2(
12
),cb2(
'2'
) {}
virtual
void
f() { cout <<
"B2::f()"
<< endl;}
virtual
void
f2() { cout <<
"B2::f2()"
<< endl;}
virtual
void
Bf2() { cout <<
"B2::Bf2()"
<< endl;}
};
class
D :
public
B1,
public
B2
{
public
:
int
id;
char
cd;
public
:
D():id(
100
),cd(
'D'
) {}
virtual
void
f() { cout <<
"D::f()"
<< endl;}
virtual
void
f1() { cout <<
"D::f1()"
<< endl;}
virtual
void
f2() { cout <<
"D::f2()"
<< endl;}
virtual
void
Df() { cout <<
"D::Df()"
<< endl;}
};
下)
typedef
void
(*Fun)(
void
);
int
** pVtab = NULL;
Fun pFun = NULL;
D d;
pVtab = (
int
**)&d;
cout <<
"[0] D::B1::_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] "
; pFun();
pFun = (Fun)pVtab[
0
][
5
];
cout <<
" [5] 0x"
<< pFun << endl;
cout <<
"[1] B::ib = "
<< (
int
)pVtab[
1
] << endl;
cout <<
"[2] B::cb = "
<< (
char
)pVtab[
2
] << endl;
cout <<
"[3] B1::ib1 = "
<< (
int
)pVtab[
3
] << endl;
cout <<
"[4] B1::cb1 = "
<< (
char
)pVtab[
4
] << endl;
cout <<
"[5] D::B2::_vptr->"
<< endl;
pFun = (Fun)pVtab[
5
][
0
];
cout <<
" [0] "
; pFun();
pFun = (Fun)pVtab[
5
][
1
];
cout <<
" [1] "
; pFun();
pFun = (Fun)pVtab[
5
][
2
];
cout <<
" [2] "
; pFun();
pFun = (Fun)pVtab[
5
][
3
];
cout <<
" [3] "
; pFun();
pFun = (Fun)pVtab[
5
][
4
];
cout <<
" [4] 0x"
<< pFun << endl;
cout <<
"[6] B::ib = "
<< (
int
)pVtab[
6
] << endl;
cout <<
"[7] B::cb = "
<< (
char
)pVtab[
7
] << endl;
cout <<
"[8] B2::ib2 = "
<< (
int
)pVtab[
8
] << endl;
cout <<
"[9] B2::cb2 = "
<< (
char
)pVtab[
9
] << endl;
cout <<
"[10] D::id = "
<< (
int
)pVtab[
10
] << endl;
cout <<
"[11] D::cd = "
<< (
char
)pVtab[
11
] << endl;
下面是对于子类实例中的虚函数表的图:
和B2中,其有B1的成员在D继承而来的。所以,如果我们使用以下语句,则会产生二义性编译错误:
D d;
d.ib = 0;
//二义性错误
d.B1::ib = 1;
//正确
d.B2::ib = 2;
//正确
中还是有两个实例,这种继承造成了数据的重复,我们叫这种继承为重复继承。重复的基类数据成员可能并不是我们想要的。所以,C++上述的“重复继承”只需要把B1的语法中加上virtual 在查看D和GCC下的程序大家可以根据我给出的程序自己仿照着写一个去试一试):
int
** pVtab = NULL;
Fun pFun = NULL;
B1 bb1;
pVtab = (
int
**)&bb1;
cout <<
"[0] B1::_vptr->"
<< endl;
pFun = (Fun)pVtab[
0
][
0
];
cout <<
" [0] "
;
pFun();
//B1::f1();
cout <<
" [1] "
;
pFun = (Fun)pVtab[
0
][
1
];
pFun();
//B1::bf1();
cout <<
" [2] "
;
cout << pVtab[
0
][
2
] << endl;
cout <<
"[1] = 0x"
;
cout << (
int
*)*((
int
*)(&bb1)+
1
) <<endl;
//B1::ib1
cout <<
"[2] B1::ib1 = "
;
cout << (
int
)*((
int
*)(&bb1)+
2
) <<endl;
//B1::ib1
cout <<
"[3] B1::cb1 = "
;
cout << (
char
)*((
int
*)(&bb1)+
3
) << endl;
//B1::cb1
cout <<
"[4] = 0x"
;
cout << (
int
*)*((
int
*)(&bb1)+
4
) << endl;
//NULL
cout <<
"[5] B::_vptr->"
<< endl;
pFun = (Fun)pVtab[
5
][
0
];
cout <<
" [0] "
;
pFun();
//B1::f();
pFun = (Fun)pVtab[
5
][
1
];
cout <<
" [1] "
;
pFun();
//B::Bf();
cout <<
" [2] "
;
cout <<
"0x"
<< (Fun)pVtab[
5
][
2
] << endl;
cout <<
"[6] B::ib = "
;
cout << (
int
)*((
int
*)(&bb1)+
6
) <<endl;
//B::ib
cout <<
"[7] B::cb = "
;
的对比):
GCC 3.4.4
|
VC++ 2003
|
[1] : B1::f1()
[1] B1::ib1 : 11
[0] : B1::f()
[4] B::ib : 0
[0] B1::_vptr->
[2] 0
ç[2] B1::ib1 = 11
[5] B::_vptr->
[2] 0x00000000
这里,大家可以自己对比一下。关于细节上,我会在后面一并再说。
的(因为VC++的相对要清楚很多,所以这里只给出VC++<span lang="ZH-CN" times="" new="" roman';="" mso-hansi-font-family:="" 'times="" roman'"="" style="padding: 0px; margin: 0px; font-family: 宋体;">的程序,GCC和GCC [0] : D::f()
[3] : D::f2()
[1] B1::ib1 : 11
[0] : D::f()
[3] : 0
[6] D::id : 100
[0] : D::f()
[9] B::ib : 0
[0] D::B1::_vptr->
[2] D::Df()
ç-4
[4] D::B2::_vptr->
[2] 00000000
该地址取值后是[7] B2::cb2 = 2
[10] = 0x00000000
[1] B::Bf()
[13] B::cb = B
|
1)
,除了一些细节上的不同,其大体上的对象布局是一样的。也就是说,先是B1
(灰色),而B
关于虚函数表,尤其是第一个虚表,GCC
的虚表比较清晰和有逻辑性。
都把B分隔符把B的布局分开。GCC中的内存布局有两个地址我有些不是很明白,在其中我用红色标出了。取其内容是-45)GCC中则没有指向B的size这门语言是一门比较复杂的语言,对于程序员来说,我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言,我们就必需要了解C++这门最难的编程语言。
本文出自 “陈皓的个人专栏” 博客,请务必保留此出处http://haoel.blog.51cto.com/313033/124561