继承(下)----虚继承

单继承&多继承

一个子类只有一个直接父类时称这种继承关系为单继承。
一个子类有两个或者两个以上的父类时称这种继承关系为多继承。

在这里插入图片描述

菱形继承 ---------特殊的多继承

在这里插入图片描述
有很大的缺点:

  • 二义性
    如上图,D中会有两份相同的数据,本质都来源于A,在调用派生类里面的这个数据时就会出现问题时就会出现二义性问题。
  • 数据冗余
    D有两个父类,则数据会很多,甚至有重复的,造成数据冗余
    在这里插入图片描述

解决办法一:加作用域可以解决二义性问题

class Person
{
public:
string _name ; // 姓名
};
class Student : public Person
{
protected :
int _num ; //学号
};
class Teacher : public Person
{
protected :
int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
string _majorCourse ; // 主修课程
};
void Test ()
{
// 显示指定访问哪个父类的成员
Assistant a ;
a.Student ::_name = "xxx";
a.Teacher ::_name = "yyy";
}

解决办法二:虚继承----解决菱形继承的二义性问题和数据冗余问题

虚继承解决二义性与数据冗余的原理

先看这个代码

class A
{
public:
	int a;
};

class B:public A
{
public:
	int b;
};
class C : public A
{
public:
	int c;
};

class D:public B, public C
{
public:
	int d;
};

int main()
{
	D d;
	d.B::a = 1;//有二义性问题,所以加作用域
	d.C::a = 2;//没加virtual时,这两个a的地址不同



	d.b = 3; 
	d.c = 4 ;
    d.d = 5;

}


查看两个a的内存,结果就是B,C里面的a内存地址单元不同
在这里插入图片描述
d中其他数据:
在这里插入图片描述
在这里插入图片描述

虚继承以后,B,C中的a的内存单元只有一个,值为2,内存单元中的值先为1,其后变为2

class A
{
public:
	int a;
};

class B:virtual public A   //虚继承
{
public:
	int b;
};
class C :virtual public A   //虚继承
{
public:
	int c;
};

class D:public B, public C
{
public:
	int d;
};

int main()
{
	D d;
	d.B::a = 1;
	d.C::a = 2;



	d.b = 3; 
	d.c = 4 ;
    d.d = 5;

}


在这里插入图片描述

在这里插入图片描述

虚函数解决二义性与数据冗余的本质----虚基表

在这里插入图片描述
在这里插入图片描述

所以虚继承的特点是虚基类共享同一个对象地址,节省了空间,也避免了数据冗余以及二义性的问题。


lass A
{
public:
	void set_a(int m)
	{
		a = m;
	}

	int a ;

};

class B :public A
{
public:
	void set_b(int n)
	{
		b = n;
	}


	int b;
};

class C:public A
{
public:

	void set_c(int n)
	{
		c = n;
	}

	int c;
};
class D :public B,public C
{
public:
	void set_d(int n)
	{
		d = n;
	}

	int d;
	
};

int  main()
{
	
	D d;
	d.set_a(200);      //a在类B和类C中都有,所以报错,调用不明确
	d.A::set_a(200);  //加上作用域可以解决这个问题
	d.set_b(300);
	d.set_c(400);
	d.set_d(500);
	system("pause");
	return 0;
}
//使用虚基类

class A
{
public:
	void set_a(int m)
	{
		a = m;
		cout << a << endl;
	}

	int a ;

};

class B : virtual public A      //虚基类
{
public:
	void  set_b(int n)
	{
		b = n;
		cout << b << endl;
	}


	int b;
};

class C: virtual public A          //虚基类
{
public:

	void  set_c(int n)
	{
		c = n;
		cout << c << endl;
	}

	int c;
};
class D :public B,public C
{
public:
	void   set_d(int n)
	{
		d = n;
		cout << d << endl;
	}

	int d;
	
};

int  main()
{
	
	D d; 
	d.set_a(200);          //设置虚基类类后不会报错
	d.set_b(300);
	d.set_c(400);
	d.set_d(500);
	cout << d.a << endl;
	

	system("pause");
	return 0;
}

在这里插入图片描述

因此菱形继承也会产生二义性和数据冗余的问题的问题,所以尽量不要设计菱形继承,否则就需要用菱形虚继承来解决问题

虚继承和直接继承有什么区别?

  1. 时间上:派生类访问虚继承的所用的时间更久,因为这是通过某种引用来完成的,增加了寻址功能。其实就是调整this指针指向虚基类对象。
  2. 空间上:由于共享所以不必在对象内存中保存多份虚基类对象的拷贝,这样比直接继承省空间。
继承于static成员函数

基类定义了一个(static)静态成员,派生类可以继承,实际上来说整个继承体系中都只有一个static成员,无论有多少个派生类,派生类和父类中的static共用空间。

class base
{
public:
	static int a;
};
int base::a = 100;

class devired  :public base
{
public:
	int d;
};

int main() 
{
	devired d;
	cout<<d.a<<endl;   //结果为100
	system("pause");

}

继承与友元

在C++中,派生类把基类中所有的成员继承过来,除了构造函数和析构函数。
友元函数不属于类,它只是给类开了一个后门
本来类外不可以访问类的私有成员,通过友元函数就可以
所以不能继承。子类继承父类,那么默认的,就是继承了父类的成员函数和成员变量。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值