C++ 继承机制易犯的错误

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

51Testing软件测试网 Q'}2Rlt'x4|3B

  继承作为面向对象的基本特征之一,其使用率极高。不管是为了实现软件的基本功能,还是再程序的重构的过程中,我们总是会用到继承机制。正是因为 其用途极为广泛,而且使用简单,大众程序员对其真正的内部实现机制的探究不是很深。而且,在大部分情况下,我们对继承的使用方法是错误的。下面用例子来说 明问题。

|"_rRF.\0 51Testing软件测试网GF&R? L

51Testing软件测试网8U\ {)K b4\;`J*O

class Animal {51Testing软件测试网}5r Ps-q7P']
   public:
~#luCc,`Aj)e0       Animal &operator=(const Animal &rhs);
HF8bZ1P0       ...
`3G9Ph!]V0};
A5W:bt;Vp jI mm0class Animal1: public Animal {
1p%t9a n-_ ~_q0   public:51Testing软件测试网K*aPC r?}f
       Animal1 &operator=(const Animal1 &rhs);51Testing软件测试网kq`,Y8S5a U2Q![ H
       ...
8^p)aPL9xc3OE!z0};
'` ?2w2h g*gs)w)pA0class Animal2: public Animal {51Testing软件测试网uNsl_7Wec/d&Q
   public:51Testing软件测试网4f6IY oB2IVom4J
       Animal2 &operator=(const Animal2 &rhs);51Testing软件测试网|7]TnaaNo0M
       ...51Testing软件测试网 V!C r E?"rvW
};

K?syj0k.spuR0  上面的代码只是简单的定义一个继承体系,即Animal作为基类,Animal1和Animal2公共继承它。三者都重载了赋值运算符。这对于问题的说明已经足够了。考虑下面的代码:51Testing软件测试网G$WOaU&e oz"k$Tv

51Testing软件测试网9_${ | e-C!~

51Testing软件测试网'Ek/g.?2c7V(T

Animal1 an1;
|[+u$GgF Q0Animal1 an2;
"Fw/ufa9N f] @A0Animal  *pAn1 = &an1;51Testing软件测试网*KH5q2\2V
Animal  *pAn2 = &an2;51Testing软件测试网.@Q U%l I$G!N ^
...
cN(I2t8~[ O#C0*pAn1 = *pAn2;

Sw U.nsp0  上面的代码足够简单了吧!问题就出现了。我们在最后一行的目的是将an2赋值给an1.如果你不是此目的,那么可以绕开本文了!因为通过指针, 对对象进行赋值动作对于c++程序员来说,非常普遍。但是实际的效果确是,an1的Animal成分与an2的Animal成分相同,而an1的 Animal1成本保持不变。这里提一下出现这种情况的原因:1、继承体系中的赋值函数是重载,而不是覆盖和隐藏(注意三者的区别:);2、由于 Animal *pAn1 = &an1,是产生pAn1所覆盖的范围缩小的效果,因此当采用赋值操作时,实际上调用的赋值函数时基类的赋值函数。这种效果是不是导致你的an1 对象二不象了,既不是原来的an1对x爱嗯,也不是你期待的an2对象。不过,如果你是想达到这种移花接木的效果,那么我恭喜你,这种用法太妙了,也说明 你对c++ 的继承体系已经到了一种登峰造极的地步。51Testing软件测试网5Gp\ q(pkNo

51Testing软件测试网Sg4?_5Wk"\b

  不过,大部分人都不是实现移花接木的功能,那么怎么实现全部成分的赋值效果呢?51Testing软件测试网zDfm;R g oL V;j q u

51Testing软件测试网 V)E3[/O:P_\T

  既然已经用到了继承机制,那么就不得离开虚函数了。我们将赋值操作符函数定义为虚函数,代码如下:

L["D9~2C#?kb0

S0M}} \,B~0

z7Y7Cy'p4p0
class Animal {51Testing软件测试网 T6x K;Ou J
   public:
;{4T1f:S!I0       virtual Animal &operator=(const Animal &rhs);51Testing软件测试网)?h/DGZ
       ...51Testing软件测试网 H/q]J{0qz.]6rO
};
5E+VnEH"zNqL0class Animal1: public Animal {51Testing软件测试网K{V0J.ge
   public:51Testing软件测试网!Ciy^1\
       virtual Animal1 &operator=(const Animal1 &rhs);
-S`:R/`$T-Zsl*n0 ...};class Animal2: public Animal { public: virtual Animal2 &operator=(const Animal2 &rhs);51Testing软件测试网s2x1z+@Z @
...};

0n8^ E6^l#rw;B0  采用虚函数确实能够解决上面提到的全部成分的赋值效果,因为他会导致覆盖赋值函数,而不是上面的重载,因此会调用实际Animal1类的赋值函数。但这样仍然会带来问题,如下的代码:

&]'FeF7v-S.w^0

T4[8} b~0

7T+o-W}%? |#p0
Animal1 an1;
]\ ZS!ujO`0Animal2 an2;//这里是Animal2对象,与前面的Animal1不同
)s:m^;^A0Animal  *pAn1 = &an1;
g3o;z%WP0Animal  *pAn2 = &an2;
9p/aA T/kG0...
aEfv h4z0\0*pAn1 = *pAn2;//将Animal2对象赋值给Animal1
51Testing软件测试网'y)h"E ~%N"u#X

  这样子会允许异型转换,明显还是会出现问题。如何解决呢?可以参考《More Effective c++》里面的条款34。

c[{ s)n-z l EP s0

TAG:

 

评分:0

我来说两句

Open Toolbar