类的继承中同名冲突及解决方案

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

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值