C++ 继承(2)

Hello!!大家早上中午晚上好!!今天收尾继承剩余部分内容!!

一、友元不能继承

基类的友元函数不能被子类继承,也就是基类的友元函数访问不了子类的私有或保护成员!

1.1解决方法在子类再声明一次友元
//基类友元不能访问子类私有和保护成员
class B;
class A
{
public:
	friend void Print(const B& b);
	int _a=0;
};
class B:public A
{
	//friend void Print(const B& b);
	const char* _ch = "你好!!";
};
void Print(const B& b)
{
	cout << b._ch << endl;//报错
}
int main()
{
	A a;
	B b;
	Print(b);
	return 0;
}

编译报错:

解决办法:在子类再声明一次友元函数

代码更改:

class B:public A
{
	friend void Print(const B& b);//子类声明友元
	const char* _ch = "你好!!";
};

再运行:

二、基类的静态成员

2.1在继承体系中,基类的静态成员,有且只有一份实例,无论派生出多少个子类;
class A
{
public:
	A() 
	{
		++cout;
	}
	static int cout;
};
int A::cout = 0;
class B:public A
{
};
class C :public B
{

};
int main()
{
	A a1;
	B b1;
	B b2;
	B B3;
	C c1;
	cout << A::cout << endl;
	cout << B::cout << endl;
	cout << C::cout << endl;
	B::cout = 0;
	cout << A::cout << endl;
	cout << B::cout << endl;
	cout << C::cout << endl;
	return 0;
}
2.2运行:

三、多继承

3.1首先理解单继承

什么是单继承?

当子类只有一个直接父类的时候属于单继承:

3.2再来理解多继承

什么是多继承?

当子类有两个或以上的直接父类的时候属于多继承:

3.3多继承衍生出的菱形继承

什么是菱形继承?

如图:

​​​​​​

3.4菱形继承衍生出数据冗余和二义性问题

写一个简单的菱形继承:

class Person
{
public:
	string _name = "张三";
};
class Student :public Person
{
public:
	int _id=12345;//学号
};
class Teacher :public Person
{
public:
	int _number=112233;//职工编号
};
class Scientist :public Student, public Teacher
{
public:
	string _subject = "物理";
};
int main()
{
	Scientist s1;
	cout << s1._name << endl;//报错
	return 0;
}

图解:

编译报错:

因为此时Scientist里有一个两个类对象,这两个类对象里都存在一个_name,编译器就不知道访问哪个,这就是菱形继承产生二义性的问题;

虽然你可以用类域访问符访问,但数据冗余问题依然存在,Scientist里面始终存在两个一样的成员变量!

用类域访问

四、菱形虚拟继承

4.1使用虚拟继承解决菱形继承存在数据冗余和二义性的问题

方法:在腰部位置添加virtual关键字(注意:除了菱形继承不要在其他位置使用虚拟继承)

代码更改:

class Student :virtual public Person
{
public:
	int _id=12345;//学号
};
class Teacher :virtual public Person
{
public:
	int _number=112233;//职工编号
};

更改完后不需要加类域访问符编译也不会报错;

4.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;
	return 0;
}

F10走起:

分析:

由此可以看出虚拟菱形继承的实现原理是:

①基类成员只生成一份放到最下面(解决了数据冗余);

②每存在一个派生类 -> 派生类对应部分首地址存放一个虚表指针 -> 这个指针指向其对应的虚基表 -> 虚基表里存放的是相对于指针当前位置A类成员的偏移量 -> 每个派生类通过其内的指针就能访问到A类成员 ,从而(解决了数据二义性)的问题!

4.3总结

回观整个问题产生及解决过程:

继承衍生出多继承 -> 多继承衍生出菱形继承 -> 菱形继承衍生出数据冗余以及二义性的问题 -> 解决办法虚拟继承 -> 虚拟继承实现原理 -> 利用虚基表指针 -> 虚基表存放偏移量 -> 通过指针访问解决问题!!

因此解决菱形继承问题是非常复杂的过程,在我们以后的程序设计尽量避免设计成菱形继承!!!

五、继承与组合的选择

5.1什么是组合?

组合就是在类里面直接使用定义好的类,不需要继承!

简单的组合使用:

class Tire//轮胎类
{
public:
	int _id = 011;//轮胎型号
	int _size = 10;//轮胎大小
};
class Car //车类
{
public:
	string _Cartype = "大巴车";//车类型
	Tire _t;//类里用轮胎类定义,不需要继承
};

简单的继承使用:

class Tire//轮胎类
{
public:
	int _id = 011;//轮胎型号
	int _size = 10;//轮胎大小
};
class Car //车类
{
public:
	string _Cartype = "大巴车";//车类型
	Tire _t;//类里用轮胎类定义,不需要继承
};

class Animal //动物类
{
public:
	char _Ability[10] = "五颗星";//运动能力
	string _structure = "多细胞有机体";//细胞结构
};
class Dog :public Animal//狗类
{
public:
	char _name[10] = "哈士奇";
	void Dance()
	{
		cout << "汪汪汪!!我会跳舞!!动次打次!!!" << endl;
	}
};
int main()
{
	Dog hashiqi;
	cout << "名字:" << hashiqi._name << endl;
	cout << "运动能力:" << hashiqi._Ability << endl;
	cout << "细胞结构:" << hashiqi._structure << endl;
	hashiqi.Dance();
	return 0;
}

运行:

总结:

①当关系为is - a 时用继承,例:哈士奇是狗,玫瑰是花,杨柳是树等等;

②当关系为has - a时用组合,例:车里有轮胎(而不能说车是轮胎),人有嘴巴(而不能说人是嘴巴),瓶子里有水(而不能说瓶子是水)等等;

5.2组合与继承的优劣

1、组合耦合度低,继承耦合度高,优先用组合;

2、继承一定程度破坏了基类的封装,基类的改变对派生类有很大的影响;派生类和基类的依赖关系很强!

好了,今天就复习到这里!!如果您觉得有所收获请点赞收藏+关注哦!!谢谢!!!

如果您有更好的意见欢迎评论区留言!!

咱下期见!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值