C++ 是一门面向对象的编程语言,其继承的特性,可以提高代码的可重用性。但是在其带来便利的同时也带来一些问题:在编程中避免不了变量名重复的情况。在C语言中,由于其没有类,也就是局部变量和全局变量的重名冲突,但是C++出现了类,同名的冲突就不止局部变量和全局变量的冲突了。
第一种,派生类和基类的同名冲突。
这种同名冲突源自于派生类在定义新成员时,新成员的名称与基类的某个成员同名。此时同名覆盖原则将发挥作用。即,如果不加特定的标识符,则访问的都是派生类的同名成员。
如果想要访问基类的同名成员,解决办法便是在重名的变量名前加“基类名::”进行限定。
但是有几个注意的事项:1、通过派生类的指针和引用的方法访问的还是派生类的同名变量。2、基类的指针指向派生类的对象时,访问的是基类的同名成员。3、基类的引用成为派生类的别名时,访问的依旧是基类的同名成员。
#include <iostream>
using namespace std ;
class Base
{
public :
Base (int x ) //基类Base的构造函数,作用给a赋值。
{
a = x ;
}
void print ()
{
cout << "基类Base的a的值是:"<< a << endl ;
}
int a ;
};
class Top :public Base //继承自Base类,公有继承。
{
public :
int a ; //声明一个同名的变量。
Top (int x ,int y ) :Base (x) //派生类构造函数,对新的a赋值。同名覆盖原则。
{
a = y ; //直接访问的是派生类中的同名变量。
Base :: a *= 2 ; //访问基类中a的方式。
}
void print ()
{
Base :: print() ; //调用基类中同名函数方式。
cout << "派生类Top的a的值是:"<<a << endl ;
}
};
void f1 (Base &obj) //传入基类Base类对象的引用。
{
obj.print() ;
}
void f2 (Top &obj) //传入派生类Top类对象的引用。
{
obj.print() ;
}
int main ()
{
Top d ( 200 , 300 ) ; //定义出一个Top类的对象
d.print () ; //无特殊标识符,访问的就是派生类中的同名函数(变量)。
d.a = 400 ;
d.Base :: a = 500 ; //加了特殊标识符,访问的是基类的同名函数(变量)。
d.Base :: print () ;
Base *bp ;
bp = &d ;
bp -> print () ; //基类类型的指针,访问的是基类的同名成员。
f1 (d) ; //基类的引用,访问基类的同名成员。
Top *pd ;
pd = &d ;
pd -> print() ; //派生类型的指针,访问的是派生类的同名成员。
f2 (d) ; //派生类的引用,访问派生类的同名成员。
return 0;
}
第二种,多个直接基类引发的同名冲突
第二种的解决方案与第一种类似,便不多解释。
第三种,也是最难的一种,共同祖先基类多重拷贝引发的同名冲突。
此时,若是依旧用前两种的方式,基类名加域解析符(::)的方法,编译器会报错,报错内容大概为访问的成员具有二义性。为什么会这样呢,有两种解释,一种是路径解释:Top的祖先Base,有两条路到达Top类,经由Base1和经由Base2。但是如果出现Base::a的语句(假设Base , Base1 , Base2 中都有一个public类型的整型变量a) ,那么编译器不知道到底是那条路去访问Base中的a ,故编译器报错。另一种解释是,要构造出Top类,首先要构造出其直接基类Base1 和Base2 ,但是Base1 和Base2从Base中继承,故Base的构造函数被执行了两次,产生了两个无名的Base类型的对象,故Base::a不知道访问那个。
对于这种的冲突,C++ 提供了一种称为虚基类的技术。虚基类的定义通过关键字virtual来实现。虚基类的作用是确保虚基类的构造函数至多被调用一次。程序运行时,系统会进行检查。如果虚基类的构造函数没有被调用过,那就调用一次,如果已经被调用了,就忽略此次调用。
注意,虚基类的使用时有几个使用事项:1、virtual放在继承方式的前边也可,放在后边也可。2、所有的虚基类的派生类在继承时都要加virtual,否则会报错。3、C++中规定,只有最后一层的派生类对于虚基类的构造函数的调用发挥所用,其他的都被忽略掉。
#include<iostream>
using namespace std;
class Base
{
protected:
int a;
public:
Base(int x) :a(x)
{
cout << "基类Base的a的值为:" << a << endl;
}
};
class Base1 :public virtual Base
{
protected:
int a;
public:
Base1(int x, int y) :Base(x), a(y)
{
cout << "派生类Base1中a的值为:" << a << endl;
cout << "在派生类Base1中的基类中的a值为:" << Base::a << endl;
}
};
class Base2 : virtual public Base
{
protected:
int a;
public:
Base2(int x, int y) :Base(x), a(y)
{
cout << "派生类Base2中a的值为:" << a << endl;
cout << "在派生类Base2中的基类中的a值为:" << Base::a << endl;
}
};
class Top : public Base1, public Base2
{
public:
Top(int x, int y , int z) : Base1(x, y), Base2(2 * x, 2 * y), Base(3 * x ) , a(z)
{
cout << "a == " << a << endl;
cout << "Base::a == " << Base::a << endl;
cout << "Base1::a == " << Base1::a << endl;
cout << "Base2::a == " << Base2::a << endl;
}
int a;
};
int main()
{
Top d(10, 20 , 50);
return 0;
}
运行结果:
基类Base的a的值为:30
派生类Base1中a的值为:20
在派生类Base1中的基类中的a值为:30
派生类Base2中a的值为:40
在派生类Base2中的基类中的a值为:30
a == 50
Base::a == 30
Base1::a == 20
Base2::a == 40