【C++】4.4 多重继承

4.4 多重继承

【定义】当一个派生类具有两个或多个基类时,这种派生方法成为多重继承或多基类派生。

4.4.1 多重继承派生类的声明

【语法形式】   

      class 派生类名:继承方式1 基类名1,.....,继承方式n 基类名n{

                   派生类新增的数据成员和成员函数     

        };

【示例】

#include<iostream>
using namespace std;

class X{
	public:
		X(int sub_x){
			x=sub_x;
		}
		void visit(){		//同名函数 
			cout<<"X->x = "<<x<<endl;
		}
		
		void setX(int set_x){
			x=set_x;
		}
	private:
		int x;
};

class Y{
	public:
		Y(int sub_y){
			y=sub_y;
		}
		void visit(){		//同名函数 
			cout<<"Y->y = "<<y<<endl;
		}
				
		void setY(int set_y){
			y=set_y;
		}
	private:
		int y;
};  

class Z:private X,public Y{	//类Z私有继承类X,公有继承类Y 
	public:
		Z(int sub_x,int sub_y,int sub_z):X(sub_x),Y(sub_y){
			z=sub_z;
		}
		void visit(){		//同名函数 
			cout<<"Z->z = "<<z<<endl;
		}
	private:
		int z;
}; 

int main()
{
	Z z(10,20,30);
	//z.X::visit();		//类Z私有继承类X,则X::visit()在派生类中的访问属性为私有,无法使用对象访问 
	z.Y::visit();		//使用作用域声明,防止二义性 
	z.Z::visit();
	
	z.setY(40);	        //类Z公有继承类Y,则Y::setY()在派生类中的访问属性为公有,可以使用对象访问
	z.Y::visit();
	return 0;
}

【运行结果】

 4.4.2 多重继承派生类的构造函数与析构函数

(1)语法形式:

派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),......,基类名n(参数表n)

{          派生类新增成员的初始化语句           }

(2)参数总表中包含完成所有基类初始化所需的参数个数

(3)构造函数执行顺序

         基类构造函数 ===>对象成员的构造函数 ===>派生类构造函数

         若存在多个基类,则按照派生类声明时从左到右的顺序调用构造函数

【示例】

#include<iostream>
using namespace std;

class X{
	public:
		X(int sub_x){
			x=sub_x;
			cout<<"constructor -> x"<<endl;
		}
		void visit_x(){		
			cout<<"X->x = "<<x<<endl;
		}
		
		~X(){
			cout<<"desconstructor -> x"<<endl;
		}
	private:
		int x;
};

class Y{
	public:
		Y(int sub_y){
			y=sub_y;
			cout<<"constructor -> y"<<endl;
		}
		void visit_y(){	
			cout<<"Y->y = "<<y<<endl;
		}
				
		~Y(){
			cout<<"desconstructor -> z"<<endl;
		}
		
	private:
		int y;
};  

class Z:public Y{	//公有继承类Y 
	public:
		Z(int sub_x,int sub_y,int sub_z):x(sub_x),Y(sub_y){
			z=sub_z;
			cout<<"constructor -> z"<<endl;
		}
		void visit_z(){	
			cout<<"Z->z = "<<z<<endl;
		}
		void visit_y(){
			Y::visit_y();
		}
		void visit_x(){
			x.visit_x();
		}
		~Z(){
			cout<<"desconstructor -> z"<<endl;
		}
	private:
		int z;		
		X x;	//类的组合,在类Z中定义类X的对象 
}; 

int main()
{
	Z z(10,20,30);		//调用构造函数 
	z.visit_x();
	z.visit_y();
	z.visit_z();
	return 0;
}

【运行结果】

4.4.3 虚基类

1、为什么要引入虚基类

      一个类继承多个基类,而基类又有同一个基类类,则最底层的派生类中保留这个共同基类数据成员的多份同名成员,在访问同名成员时,必须在前加上基类名,否则将产生二义性。

【示例】

#include<iostream>
using namespace std;

class Base{
	public:
		Base(){
			a=10;
			cout<<" Base->a = "<<a<<endl;
		}
		
	protected:
		int a;
}; 

class Base1:public Base{
	public:
		Base1(){
			a=a+10;
			cout<<" Base1->a = "<<a<<endl<<endl;
		}
		

};

class Base2:public Base{
	public:
		Base2(){
			a=a+20;
			cout<<" Base2->a = "<<a<<endl<<endl;
		}

};

class Derived:public Base1,public Base2{
	public:
		Derived(){
			cout<<" Base1->a = "<<Base1::a<<endl;
			cout<<" Base2->a = "<<Base2::a<<endl;
		}
		
};

int main()
{
	Derived obj;
	return 0;
}

【运行结果】

【类层次图】

由类层次图可知,类Base1和类Base2同时存在同名的数据成员a,他们都是类Base成员的复制

但类Base1和类Base2中数据成员a具有不同的存储单元,存放不同的数据

由于类Derived中同时存在类Base1和类Base2的数据成员,因此在Derived构造函数中输出a的值,必须加上“::”作用域运算符

因此,为了解决二义性,C++引入虚基类

2、虚基类的概念

(1)C++中,使公共基类只产生一个复制,则可以将这个基类说明为虚基类

(2)语法形式:

class 派生类名:virtual 继承方式 基类名{
.....
}

【示例】

#include<iostream>
using namespace std;

class Base{
	public:
		Base(){
			a=10;
			cout<<" Base->a = "<<a<<endl;
		}
		
	protected:
		int a;
}; 

class Base1:virtual public Base{
	public:
		Base1(){
			a=a+10;
			cout<<" Base1->a = "<<a<<endl<<endl;
		}
		

};

class Base2:virtual public Base{
	public:
		Base2(){
			a=a+20;
			cout<<" Base2->a = "<<a<<endl<<endl;
		}

};

class Derived:public Base1,public Base2{
	public:
		Derived(){
			cout<<" Base1->a = "<<Base1::a<<endl;
			cout<<" Base2->a = "<<Base2::a<<endl;
		}
		
};

int main()
{
	Derived obj;
	return 0;
}

【运算结果】

由运算结果可知,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次,可知Base中的a只保留一次。

【类层次图】

(3)虚基类与构造函数

【示例】

#include<iostream>
using namespace std;

class Base{
	public:
		Base(int sa){
			a=sa;
			cout<<" Base Constructor Called"<<endl;
		}
		
	protected:
		int a;
}; 

class Base1:virtual public Base{
	public:
		Base1(int sa,int sb1):Base(sa){
			b1=sb1;
			cout<<" Base1 Constructor Called"<<endl;
		}
	protected:
		int b1;
		

};

class Base2:virtual public Base{
	public:
		Base2(int sa,int sb2):Base(sa){
			b2=sb2;
			cout<<" Base2 Constructor Called"<<endl;
		}
	protected:
		int b2;

};

class Derived:public Base1,public Base2{
	public:
		Derived(int sa,int sb1,int sb2,int sd):Base(sa),Base1(sb1,sa),Base2(sb2,sa){
			d=sd;
			cout<<" Derived Constructor Called"<<endl;
		}
	protected:
		int d;
		
};

int main()
{
	Derived obj(1,2,3,4);
	return 0;
}

【运行结果】

【注意】

示例中:

【1】Base是一个虚基类,它只有一个带参数的构造函数,因此要求在派生类Base1、Base2和Derived的构造函数的初始化列表中,都必须带有对类Base的构造函数的调用。

【2】如果Base不是一个虚基类,在派生类Derived中的构造函数的初始化列表中调用Base的构造函数是错误的

【原因】:派生类只负责直接基类的构造函数的调用,即对直接基类中的成员变量赋值

【3】虚基类Base的构造函数只被Derived的构造函数调用一次,而Base1和Base2的调用被忽略。

 

(4)虚基类使用说明

【1】一个基类可作为派生类的虚基类同时,也可以作为派生类的非虚基类

【2】virtual与继承方式关键字的先后顺序无关紧要

         virtual public或public virtual都是合法的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值