虚拟继承

      虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如下图所示。
 
                                                      
       类D继承自类B1、B2,而类B1、B2都继承自类A,因此出现如上图(b)所示的局面(非虚基类)。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。最后形成如上图(a)所示的情况。实现代码如下:
class A;//忽略C1和C2
class B1 : public virtual A;   //注意关键字virtual
class B2 : public virtual A;   //注意关键字virtual
class D : public B1,public B2;

一、为什么要引入虚拟继承
     虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在C++中,多重继承是不推荐的,也并不常用,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要。请看一个例子:
#include <iostream>
#include <memory.h>
class CA
{
	int k; //如果基类没有数据成员,则在这里多重继承编译不会出现二义性
public:
	void f() {cout << "CA::f" << endl;}
};
class CB : public CA{ }; //不是虚拟继承
class CC : public CA{ }; //不是虚拟继承
class CD : public CB, public CC{ };
void main()
{
	CD d;
	d.f();
}
程序运行出错:
error C2385: 对“f”的访问不明确
	 可能是“f”(位于基“CA”中)
	 也可能是“f”(位于基“CA”中)
    即编译器无法确定你在d.f()中要调用的函数f到底是哪一个。这里可能会让人觉得有些奇怪,命名只定义了一个CA::f,既然大家都派生自CA,那自然就是调用的CA::f,为什么还无法确定呢?
    这是因为编译器在进行编译的时候,需要确定子类的函数定义,如CA::f是确定的,那么在编译CB、CC时还需要在编译器的语法树中生成CB::f,CC::f等标识,那么,在编译CD的时候,由于CB、CC都有一个函数f,此时,编译器将试图生成这两个CD::f标识,显然这时就要报错了。(当我们不使用CD::f的时候,以上标识都不会生成,所以,如果去掉d.f()一句,程序将顺利通过编译)
要解决这个问题,有两个方法:
第一个:重载函数f()。
class CD : public CB, public CC
{
public:
	void f() {cout << "CD::f" << endl;}
};

第二个:使用虚拟继承。虚拟继承又称共享继承,这种共享其实也是在编译期间实现的,当使用虚拟继承时,上述程序变为:
#include <iostream>
#include <memory.h>
class CA
{
	int k;
public:
	void f() {cout << "CA::f" << endl;}
};
class CB : virtual public CA{ };  //也有一种写法是class CB : public virtual CA
                                  //实际上这两种方法都可以
class CC : virtual public CA{ };  //使用了虚拟继承
class CD : public CB, public CC{ };
void main()
{
	CD d;
	d.f();
}

      程序没有出错了。调用的f函数是CA::f()。
二、虚拟基类的初始化
      虚拟基类的初始化是有最终派生类完成。而这个最终派生类是由每个特定类对象的声明来决定的。具体是什么意思呢?让我们来看一个具体的例子:
 
class ZooAnimal  //虚拟基类
{
public:
	ZooAnimal(string name,bool onExhibit,string fam_name) : _name(name),_onExhibit(onExhibit),_fam_name(fam_name)
	{

	}
	virtual ~ZooAnimal() ;
	virtual ostream& print(ostream&) const ;
	string name() const {return _name ;}
	string family_name() const {return _fam_name ;}
	// ...
protected:   //类的数据成员声明为protected
	string _name ;
	bool _onExhibit ;
	string _fam_name ;
	// ...
};

class Bear : public virtual ZooAnimal  //虚拟继承
{
public:
	enum DanceType{two_left_feet,macarena,fandango,waltz} ;
	Bear(string name,bool onExhibit = true) : ZooAnimal(name,onExhibit,"Bear"),_dance(two_left_feet)
	{

	}
	virtual ostream& print(ostream&) const ;
	void dance(DanceType) ;
	// ...
protected:   //类的数据成员声明为protected
	DanceType _dance ;
};

class Raccoon : public virtual ZooAnimal  //虚拟继承
{
public:
	Raccoon(string name,bool onExhibit = true) : ZooAnimal(name,onExhibit),_pettable(false)
	{

	}
	virtual ostream& print(ostream&) const ;
	bool pettable() const {return _pettable ;}
	void pettable(bool petval){_pettable = petval ;}
	// ...
protected:   //类的数据成员声明为protected
	bool _pettable ;
	// ...
};

class Panda : public Bear,public Raccoon,public Endangered
{
public:
	Panda(string name,bool onExhibit = true) ;
	virtual ostream& print(ostream&) const ;

	bool sleeping() const {return _sleeping;}
	void sleeping(bool newval){ _sleeping = newval ;}
	// ....
protected:   //类的数据成员声明为protected
	bool _sleeping ;
};
       上面的派生是虚拟派生。在非虚拟派生中,派生类只能显示的初始化其直接基类。且,ZooAnimal的非虚拟派生中,Panda类不能在Panda成员初始化表中直接调用ZooAnimal的构造函数,因为ZooAnimal不是Panda的直接基类。然而,在虚拟派生中,只有Panda可以直接调用其ZooAnimal虚拟基类的构造函数。
       那么,虚拟基类的初始化有其最终派生类来初始化,而这个最终派生类是由每个特定类对象的声明来决定的。例如:

我们在声明Bear类对象时:
	Bear winnie("pooh") ; //声明Bear类对象,所以Bear是winnie对象的最终派生类
Bear是winnie对象的最终派生类,它所调用的ZooAnimal构造函数被执行。当我们写如下语句时:
	cout << winnie.family_name() ;
输出的是:
	The family name for pooh is Bear.
类似的,如下声明:
	Raccoon meeko("meeko") ;  //声明Raccoon类对象,所以Raccoon是meeko对象的最终派生类
声明Raccoon是meeko类对象的最终派生类,因此应执行Raccoon调用的ZooAnimal构造函数。当我们写下如下语句时:
	cout << meeko.family_name() ;
输出的是:
	The family name for meeko is Raccoon.
现在,当我们声明Panda对象时,比如:
	Panda yolo("yolo") ;  //声明Panda类对象,所以Panda是yolo对象的最终派生类
Panda是yolo类对象的最终派生类,所以初始化ZooAnimal称为Panda类的责任。
当一个Panda对象初始化时:1、在Raccoon和Bear的构造函数执行过程中,它们对于ZooAnimal构造函数的调用
不再执行。2、ZooAnimal构造函数被调用时,其实参是在Panda的初始化表中被指定。下面是Panda构造函数的具体实现:
Panda::Panda(string name,bool onExhibit = true)
            : ZooAnimal(name,onExhibit,"Panda"),
			  Bear(name,onExhibit),
			  Raccoon(name,onExhibit),
			  Endangered(Endangered::E_DAO_XtrieveEnvironmentError,Endangered::critical)
			  _sleeping(false) 
{

}
在Panda中,Bear和Raccoon类都被用作中间派生类而不是最终派生类。作为中间派生类,所有对虚拟基类构造函数
的调用都被自动抑制了。

      上面的例子,很好的解释了虚拟基类的初始化变成了最终派生类的责任,这个最终派生类是由每个特定类对象的声明来决定的。特别是最后一句的理解。由特定类对象的声明来决定的,上面的程序很好的解释了这个意思。
 
详细具体的介绍可见: http://baike.baidu.com/view/825838.htm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值