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、继承一定程度破坏了基类的封装,基类的改变对派生类有很大的影响;派生类和基类的依赖关系很强!
好了,今天就复习到这里!!如果您觉得有所收获请点赞收藏+关注哦!!谢谢!!!
如果您有更好的意见欢迎评论区留言!!
咱下期见!!!