C++ 虚继承对基类构造函数调用顺序的影响-2

上一篇 / 下一篇  2012-10-09 10:44:21 / 个人分类:C++

51Testing软件测试网;VK? h5TYc)q

  编译器提示调用f方法错误。而采用51Testing软件测试网:K(STB T J

_/tHhvi)[4_5L0

'G9X'_M wvjsh4K]0
ii.istream::f();
51Testing软件测试网%c:X^e8qmR

  编译通过,并且会调用istream类vptr指向的f()方法。 前面说了这么多,在实际的应用中虚拟继承的胡乱使用,更是会导致继承顺序以及基类构造顺序的混乱。如下面的代码:

6e0OM0Hi Gg*w5TP6xr0 51Testing软件测试网u6I1a} Xws9f2y\&A

51Testing软件测试网ED$q/f/ShV4pX,~ \

}d{8ePKpM MQ0class B1
O!n Y1|1~z d L0{
1Hcd.c/C s8g5t0public:
j z i$W7a0    B1(){cout<<"B1::B1()!<"<<endl;}51Testing软件测试网 ZK5bj7X_JM
    void f() {cout<<"i'm here!"<<endl;}
vY5wi:H8L;q G Ls0};

4_6U^Jj2YX!?J0 51Testing软件测试网(Q3Z7Z'Wf

class V1: public B151Testing软件测试网-zsmP{.k
{51Testing软件测试网u(s M$LE.\s/{
public:
TV2o;Y:l{+J.FI;fD0    V1(){cout<<"V1::V1()!<"<<endl;}
9_8_o9l$SZ)h0};

M \mU?R#c0

E{g;E[^sc!b#D{0class D1: virtual public V1
!SH d.c?@j0{51Testing软件测试网Wh+Z-nLQ7mbr
public:
sgRsH(q a0    D1(){cout<<"D1::D1()!<"<<endl;}
hK5}5rO0};

8Ngu0~8}dc&U3F0 51Testing软件测试网(Hfj9vF.]8q(V8r+xH

class B251Testing软件测试网B!o8|5Lq
{51Testing软件测试网@7i"X3u/g
public:
0N Z5pq!pZ:~,t)y1I0    B2(){cout<<"B2::B2()!<"<<endl;}51Testing软件测试网Qk$C3c`s
};
51Testing软件测试网?8a9HlK-URG|

51Testing软件测试网o.q)f c'B {

class B3
:iIwg/@0{51Testing软件测试网wPjn'v&G @:WgUE
public:
fbo]D&x0    B3(){cout<<"B3::B3()!<"<<endl;}
!g}-mG4P%kGq0};
51Testing软件测试网9f/I6sWy!o `6cu9b:_

Bg*h8J.XE8K,l)?q.C0class V2:public B1, public B251Testing软件测试网S e{G$Hw
{51Testing软件测试网1Z^,nrME'KB-z v
public:51Testing软件测试网)mZAW%N'`x
    V2(){cout<<"V2::V2()!<"<<endl;}
,m$B CF+w/O4TIftQ0};
51Testing软件测试网'n(S4`#X/\,m vN-yY

.eY$Nm*sJ6Q6X0class D2:virtual public V2, public B3
0V&_#V w#Jn7Y0{
!r.lUZ Xi$J o1y0public:51Testing软件测试网(] Q$@YGg'hu
    D2(){cout<<"D2::D2()!<"<<endl;}51Testing软件测试网3@#ctk eA
};

!XT!G:@a u"h0 51Testing软件测试网)e})`#W ?{6V:~

class M151Testing软件测试网2NXjX4E
{
9T/v.y6}L0X0public:
c{M'{vH0Q4b6G0    M1(){cout<<"M1::M1()!<"<<endl;}
3f+j"rDhy2X,i0E9{0};
51Testing软件测试网T L$R'w3G$?#KP+lJA

'ZO.L5T G^'P$yl0class M251Testing软件测试网-Nnh\Oy&q-F1\4F
{51Testing软件测试网i#q:v8l e/Jr[{7j
public:51Testing软件测试网)n {.k.t/fX$[5[0`5a
    M2(){cout<<"M2::M2()!<"<<endl;}51Testing软件测试网idRL Z1FF A?g
};
51Testing软件测试网]@1u6@f-H.zO

51Testing软件测试网4SE%^.N5BK-f ]@8? j

class X:public D1, public D251Testing软件测试网E+o#|'R"o,m4@
{
7b:q(J5Te&l;NnY0    M1 m1;51Testing软件测试网/[1]Q-st@
    M2 m2;
&U Q"}c ]$M-S B0};51Testing软件测试网"~ZE&w&f|R#B
int main(int argc, const char * argv[])
}#u#[{;K[,u0{
mF]:Gf^)E'P Q0    X x;51Testing软件测试网q7s.XC;o;VrD6a

bP8k0e} d H0

$o D ~r5a0  上面的代码是来自《Exceptional C++ Style》中关于继承顺序的一段代码。可以看到,上面的代码继承关系非常复杂,而且层次不是特别的清楚。而虚继承的加入更是让继承结构更加无序。不管怎 么样,我们还是可以根据c++的标准来分析上面代码的构造顺序。c++对于创建一个类类型的初始化顺序是这样子的:

mAFk9j v)W*B,b0

9R8s@ ?-J[0  1、最上层派生类的构造函数负责调用虚基类子对象的构造函数。所有虚基类子对象会按照深度优先、从左到右的顺序进行初始化;

3[`1oh4S9k9b)^0

:s$p(?Ew~oJ0  2、直接基类子对象按照它们在类定义中声明的顺序被一一构造起来;51Testing软件测试网E/lq ZV%m

_i3F;Z{#A'n+V/Y0  3、非静态成员子对象按照它们在类定义体中的声明的顺序被一一构造起来;51Testing软件测试网5VG%UY Tcqo Jd

#S,@ Q A8P@\+@(X0  4、最上层派生类的构造函数体被执行。51Testing软件测试网\ Pl5C5g"s p

eq(^3KLBz*yU0  根据上面的规则,可以看出,最先构造的是虚继承基类的构造函数,并且是按照深度优先,从左往右构造。因此,我们需要将继承结构划分层次。显然上 面的代码可以认为是4层继承结构。其中最顶层的是B1,B2类。第二层是V1,V2,V3。第三层是D1,D2.最底层是X。而D1虚继承V1,D2虚继 承V2,且D1和D2在同一层。所以V1最先构造,其次是V2.在V2构造顺序中,B1先于B2.虚基类构造完成后,接着是直接基类子对象构造,其顺序为 D1,D2.最后为成员子对象的构造,顺序为声明的顺序。构造完毕后,开始按照构造顺序执行构造函数体了。所以其最终的输出结果为:51Testing软件测试网2sk SZ{ Bd

'b1v8t?!q1KI0  B1::B1()!<
*l~a!el0  V1::V1()!<51Testing软件测试网x.zI-? e TQ1{m
  B1::B1()!<
rNpfPc Rk0  B2::B2()!<51Testing软件测试网|#Q4v[Yqs
  V2::V2()!<
:Gw^"ks%_ pc0  D1::D1()!<51Testing软件测试网7\ ~pFU)X
  B3::B3()!<51Testing软件测试网@$QR_;ve6q+M^2@
  D2::D2()!<51Testing软件测试网ifX6c^x2v xy
  M1::M1()!<51Testing软件测试网:q7B!M[S r{
  M2::M2()!<

$[&A q3bK0 51Testing软件测试网 a*V+e8K#XL R X)N

  从结果也可以看出其构造顺序完全符合上面的标准。而在结果中,可以看到B1重复构造。还是因为没有按照要求使用virtual继承导致的结果。要想只构造B1一次,可以将virtual全部改在B1上,如下面的代码:

"`_r4S6CJDqj0

_Jf&SZ_0 51Testing软件测试网 a%CA-CFi%ag_

d xn!s8yr0class B1
7z`-b qRGp:O W0{
:}Y \$Jc*^ f.Gq0public:
c2F+GA G0    B1(){cout<<"B1::B1()!<"<<endl;}
gjI]y#s8w0    void f() {cout<<"i'm here!"<<endl;}
mySlliJ0};
51Testing软件测试网 O u e6Y.b"A y%j

4_cJNQgV4v0class V1: virtual public B1   //public修改为virtual51Testing软件测试网%R3fT tt ~2H vS)m
{51Testing软件测试网&}:r;n6^4N3P
public:51Testing软件测试网f0MDV/v#w"B
    V1(){cout<<"V1::V1()!<"<<endl;}
4Y3p:GJ#U6Z0};
51Testing软件测试网$g#NF aD%j*LI:kV S

51Testing软件测试网c zzpiO8SM

class D1:  public V1
6D"bj[Oao0{51Testing软件测试网~,Xf zrZ`FY
public:51Testing软件测试网sR b(}7PK
    D1(){cout<<"D1::D1()!<"<<endl;}51Testing软件测试网/X*I+ef&Pf8YvY
};

5D0T/Q%j's/wU XN5q0 51Testing软件测试网CD!d f,^ e`7U1o? h

class B251Testing软件测试网[:j5]X.F"qyu$|
{
P2o/Vk%MG9v.r0public:51Testing软件测试网&y7K)T5I)u(o){
    B2(){cout<<"B2::B2()!<"<<endl;}
-s!?M9b d+x@M0};
51Testing软件测试网6r%[5[ V%U4^2U

'W Li1] `0class B351Testing软件测试网YqQ6l$m}
{51Testing软件测试网T9v N(@7b.nh9vhfv
public:51Testing软件测试网4AAa;TT g1VV
    B3(){cout<<"B3::B3()!<"<<endl;}
&R-b X.h6[2Ux2~0};

#} Ae JH2fC\:x$F0 51Testing软件测试网)@ d'd/qyl+y

class V2:virtual public B1, public B2 //public B1修改为virtual public B151Testing软件测试网P6}y1_p _Ug
{51Testing软件测试网$QtjV L!bW
public:51Testing软件测试网B%}b5KZq JF6H
    V2(){cout<<"V2::V2()!<"<<endl;}51Testing软件测试网]'r o(qh._ _@-D R
};

y1@x/dY}T0 51Testing软件测试网7tE2os'l;ms-tP

class D2: public V2, public B351Testing软件测试网 _h/ivE;y G
{
Ir'zj0{PQ0public:
pm2P]R [;c0    D2(){cout<<"D2::D2()!<"<<endl;}51Testing软件测试网(b*sX U(dO
};

d[*E s d0

Zt ~'W^'z0class M1
$Bf(e@1S:{0{
zJ5B'D.]0W'D0public:
4t uv'J7Ar9ab |0    M1(){cout<<"M1::M1()!<"<<endl;}
/I6XOb7k)C s0};
51Testing软件测试网:s0}(P]!F)]VA

51Testing软件测试网W]P GXz

class M2
u4x&j,~ uB7K0{51Testing软件测试网 wJm9VtL0p"{-D$D
public:
4MU,ZB4T MGg x0    M2(){cout<<"M2::M2()!<"<<endl;}
Q+Sh-dUy%M:vF:S0};
51Testing软件测试网gk6H1t4R$h0C

51Testing软件测试网JV|CO*E|

class X:public D1, public D2
9p Tpq+SsS-]/B!p0{51Testing软件测试网r"q*z"i w5?D {
    M1 m1;
} f_+n2gw!qL i8t:E0    M2 m2;
V1{+^&y2nH.N"r7U0};

;` e.v$GH0

~d` V^I0  根据上面的代码,其输出结果为:

]7h{.x X'hGe)`0

1[{"~%d~c,nx0  B1::B1()!<51Testing软件测试网6uv6Vw+o'l+PqD
  V1::V1()!<51Testing软件测试网5fU&B+R+x
  D1::D1()!<51Testing软件测试网E$`QvA(R8Dz(nT
  B2::B2()!<51Testing软件测试网;Vg"E:CbhW
  V2::V2()!<51Testing软件测试网6t!K hT6KF
  B3::B3()!<51Testing软件测试网2spW1Mx\!M
  D2::D2()!<51Testing软件测试网#YC~"m%?O
  M1::M1()!<51Testing软件测试网d1MZzI/y?R(N
  M2::M2()!<
51Testing软件测试网 Y1?j6Y:K&M]!Ky#d4B

r:D ^~N"wLP5E:T0  由于虚继承导致其构造顺序发生比较大的变化。不管怎么,分析的规则还是一样。

%d N2GI:h|Dv-d0

Xmo(R3E dy&X.A0  上面分析了这么多,我们知道了虚继承有一定的好处,但是虚继承会增大占用的空间。这是因为每一次虚继承会产生一个vptr指针。空间因素在编程 过程中,我们很少考虑,而构造顺序却需要小心,因此使用未构造对象的危害是相当大的。因此,我们需要小心的使用继承,更要确保在使用继承的时候保证构造顺 序不会出错。下面我再着重强调一下基类的构造顺序规则:

d VG3}$g2Jm0 51Testing软件测试网$X6oT6p Ue0wx

  1、最上层派生类的构造函数负责调用虚基类子对象的构造函数。所有虚基类子对象会按照深度优先、从左到右的顺序进行初始化;51Testing软件测试网 YB)ImG&A

,E8NXE?t*z0  2、直接基类子对象按照它们在类定义中声明的顺序被一一构造起来;51Testing软件测试网!|1OT&Z*B'g o3|{,R?

51Testing软件测试网 ]/N#D a(Y{6\)z

  3、非静态成员子对象按照它们在类定义体中的声明的顺序被一一构造起来;51Testing软件测试网V)fD,q:~ \/l

xo:R]5bG!o0  4、最上层派生类的构造函数体被执行。

*A"zH;{4f$s2T1\w `0

0WP;Ic2S051Testing软件测试网Z n*Q)h*Z'v+C


TAG:

 

评分:0

我来说两句

Open Toolbar