目录
同名隐藏
第一种情况,上代码
#include<iostream>
using namespace std;
class Base1
{
public:
void fun() const {cout<<"Base1::fun"<<endl;}
};
class Base2
{
public:
void fun() const {cout<<"Base2::fun"<<endl;}
};
class Base3:public Base1,public Base2
{
public:
int x,y;
//using Base1::fun;
//using Base2::fun;
//void fun() {cout<<"Base3::fun"<<endl;}
};
void main()
{
Base3 base3;
base3.fun();
base3.Base1::fun();
base3.Base2::fun();
system("pause");
}
//用using关键字来破除同名隐藏
首先,上面的程序中,base3.fun();运行起来是会报错的,个人理解就是跟重载函数相关,因为你从两个基类继承过来的同名函数的函数定义式是一样的,这时候就产生了二义性,所以编译器无法确实是要调用哪个函数,就报错了。要是在派生类中把我注释掉的void fun() {cout<<"Base3::fun"<<endl;}运行起来,就不会报错了,子类覆盖了父类里的同名函数,编译器就不会左(Base1)右(Base2)为难了,直接就调用了派生类中的fun()函数。当然,用作用域分隔符也可以很好地解决这个问题,我在上一篇博客用已经做过说明。
我再在上面的代码中也写到了,可以用using关键字来破除同名隐藏,你在派生类Base3中把using Base::fun;调用起来,就相当于你明确告诉编译器你到底需要调用哪个类里的函数,读者也可思考同时把两个基类中的fun函数同时用using关键字声明会发生什么事情(记得注释掉void fun() {cout<<"Base3::fun"<<endl;}这行语句),请读者自行思考。
还可以通过重载方式来解决,
第二种情况,上代码
#include<iostream>
using namespace std;
class Base0
{public:
int var0;
void fun0() {cout<<"Base0::fun0"<<endl;}
};
class Base1:public Base0
{
public:
int var1;
};
class Base2:public Base0
{
public:
int var2;
};
class Derived:public Base1,public Base2
{
public:
int var;
//void fun0() {cout<<"Derived::fun"<<endl;}
};
void main()
{
Derived d;
d.Base1::fun0();
d.Base2::fun0();
d.fun0();
system("pause");
}
由于Base1、Base2派生类继承了基类Base0的属性(私有变量)和行为(成员函数),并且在两个类中有两个不同的版本,如果派生类Derived中不对fun0进行定义,编译器会报错(d.fun();会报错),它不能确定调用哪个版本。这种情况下也是同样的需要通过作用域分辨符来辨别到底调用哪个版本了。
在大多数情况下,我们都是只需要一个副本的,我们会通过虚基类技术来克服这一问题。
虚基类技术
#include<iostream>
using namespace std;
class Base0
{public:
int var0;
void fun0() {cout<<"Base0::fun0"<<endl;}
};
class Base1:virtual public Base0 //虚基类的声明
{
public:
int var1;
};
class Base2:virtual public Base0 //虚基类的声明
{
public:
int var2;
};
class Derived:public Base1,public Base2
{
public:
int var;
//void fun0() {cout<<"Derived::fun"<<endl;}
};
void main()
{
Derived d;
d.fun0(); //直接访问Base0中的成员函数
d.var0=1; //直接访问Base1中的数据成员
cout<<"var0:"<<d.var0<<endl;
system("pause");
}
在继承的时候用virtual关键字来继承基类,可以很好地解决多副本问题,这种继承方式就可以让最终的继承类只含有基类的一个副本,在派生类Derived中调用成员函数的时候不会出现无法识别的问题。这就是所谓的虚基类技术。
相比之下,前者可以存储更多的数据,后者使用更加简洁,需要更少的内存,具体的操作还是要根据实际的情况来定。