#运行结果:
##现在来看通过多重继承中,出现同名类成员时的访问机制
##下面一一分析:
int main(array<System::String ^> ^args)
{
D tmp( 10, 20, 30, 40 );
tmp.fun_d( );
tmp.fun( );
tmp.B::fun( );
tmp.C::fun( );
system("pause");
return 0;
}
首先来看tmp.fun_d( );tmp是一个第三继承类对象,它讲调用哪个成员函数呢?首先它会从当前域,及它所属的类区域查找,若查找不到对应的成员函数,它会向前查找。此处tmp所属的类D中已然已经定义了对应的成员函数fun_d( ),所以这里会直接调用 tmp.D::fun_d( );
再来看tmp.fun( ).类似上面,它首先从当前域即类D中查找fun()函数,若查找不到,会向前查找,直到找到。基类A中定义了fun(),所以这里会调用属于基类A的成员函数。
再来看tmp.B::fun( ).. 很显然,第三继承类D,第二继承类B 中都没有显式定义函数fun( ),这里的调用,意欲何为?由于类B由基类A公有派生而来,所以累B会继承类A 的成员,类似于基类A会在继承类B上映射相应的成员到相应的区域。(通过公有继承, 基类A会把public/protected 成员投射到继承类B相应的public/protected区域上,注意,是一对一投射。)。所以tmp.B::fun( )暗藏着继承机制的原理,及它所调用的实际上是类A的对应成员。
当然tmp.C::fun( )这个的访问机制同上。。
问题:当类C是通过私有继承机制,来继承类A时,这种访问会是什么样?
#运行结果:
##下面稍微修改下:
##注意,此时第三继承类对象tmp调用的成员函数统一都为fun()了;没有特例D::fun_d( )了。下面来区分下调用机制。
首先来看tmp.B::fun();
tmp.C::fun(); //这里和前面的访问机制是一样的,不在赘述。
重点来看tmp.fun();不加所属类限定域时。回顾一下,当我们在局部区域定义一个和全局区域同名的变量时,当程序进入局部区域时,是如
何区分同名混淆的问题就知道。当程序查找fun()是在那个区域被定义过时,首先从局部区域出发,如果局部区域没有,向前查找,若一直未查找到,编译器会提示出错。这里程序首先就发现了D::fun().查找成功,边不再向前查找了
##运行结果:
##下面来看,动态编译时,系统如何区分基类和派生类中出现同名成员函数的问题,依旧使用上面的例子,略作修改:
##现在来看,在基类A中出现了virtual void fun( )const ; 这就是本片段的重点:虚函数(实现动态编译的基础)。
当派生类中出现功能与基类中功能相近的函数调用时,我们就可以把基类中该函数设置成虚函数,在派生类中直接显示调用基类的这个功能
函数,然后添加本函数(即派生类)中需要独立实现的功能语句,以此类推,第三派生,第四派生,等等。当然最好使用公有派生,这样可
以高效的实现代码重用。
##下面切换到具体问题上具体分析:
首先来看主函数:
const int Lim=4;
int main(array<System::String ^> ^args)
{
A first( 10 );
B second( 20,30 );
C third( 30, 40 );
D fouth( 60, 70, 80, 90);
A *po[Lim]={ &first, &second, &third, &fouth };
for ( int i=0; i<Lim; i++ )
po[i]->fun( );
system("pause");
return 0;
}
1、首先显式构建了基类,第二派生类(两个),第三派生类(D)的四个对象first,second,third,fouth.并且声明了一个基类指针数组(顾
名思义:数组p0[Lim]里面保存的是四个指针,什么指针?指向基类和三个派生类对象地址的指针,根据C++派生类对象可以向上转换成基
类对象原则(附注:向下转换是不理智的,因为派生类可能新增加了新的成员,而这些基类是不具备的) ) 。然后显式调用函数fun(),
(注意: 在基类中fun()被声明为虚函数,所以在派生类中同名函数前可不比添加virtual关键字,这个系统内部是会区分的)根据虚函数调
用原则,例:po[i]->fun() /*当i=2时,这时候po(2)==&third */,系统将根据指针所指向的对象的类型来选择调用哪个函数,而不是根
据指针的类型来判断,所以这里将调用C::fun( )。以此类推,当i=0,1,2,3时以此对调用属于类A,B,C,D中的fun()函数。
下面是运行结果:
##分析下运行结果,首先只看运行结果的前9行:
1、构建A的对象first时将显示Member of class A 这个显而易见。
2、构建B,C类的各自对象时,都将分别先调用各自基类的构造函数,在这里是A(基类),然后调用本类的构造函数,来初始化本类所新增加的成员,所以结果的2,3,4,5行就很明确了。
3、下面是构建类D的对象fouth时,由于类D是从类B,C中抽象出来的,所以生成类D的对象必然会调用类B,,C的构造函数来初始化类D继
承而来的成员,所以类B , C 都用的虚拟继承类A,这样类D构建对象时就不会再调用类B ,C 时两次调用类A了。