继承
我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性,这个时候我们就可以考虑利用继承的技术,减少重复代码。
1、继承的基本语法
例如:
我们看到很多网站中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同。
总结:
继承的好处:可以减少重复的代码
class A : public B;
A类称为子类 或 派生类
B类称为父类 或 基类
派生类中的成员,包含两大部分:
一类是从基类继承过来的,一类是自己增加的成员。
从基类继承过来的表现其共性,而新增的成员体现了其个性。
2、继承方式
继承的语法:class 子类 : 继承方式 父类
如:
class Son : public Base;
class Son : private Base;
继承方式一共有三种:
1.公共继承
2.保护继承
3.私有继承
3、继承中的对象模型
问题:从父类继承过来的成员,哪些属于子类对象中?
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C; //私有成员只是被隐藏了,但是还是会继承下去
};
//公共继承
class Son :public Base
{
public:
int m_D;
};
void test01()
{
//输出会是16 继承的m_A,m_b,m_C,和自己的m_D 4个int类型
//父类中的所有非静态成员属性都会被子类继承下去
//父类中私有成员属性 是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了
cout << "sizeof Son = " << sizeof(Son) << endl;
}
int main() {
test01();
return 0;
}
4、继承中构造和析构顺序
子类继承父类后,当创建子类对象,也会调用父类的构造函数。
问题:父类和子类的构造和析构函数顺序是谁先谁后?
构造顺序:先构造父类,再构造子类
析构顺序:先析构子类,再析构父类
5、继承同名成员处理方式
问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据或函数呢?
1.访问子类同名成员,直接访问即可
s.m_A
s.func();
2.访问父类同名成员,需要加作用域
s.Base::m_A
s.Base::func();
3.如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数,包括父类中重载的成员函数。
子类不能直接访问,需要加作用域!
6、继承同名静态成员处理方式
问题:继承中同名的静态成员在子类对象上如何进行访问?
静态成员和非静态成员出现同名,处理方式一直
1.访问子类同名成员,直接访问即可
2.访问父类同名成员,需要加作用域
1.通过对象访问
s.m_A
s.Base::m_A
2.通过类名访问
son::m_A
Son::Base::m_A
第一个::代表通过类名方式访问, 第二个::代表访问父类作用域下的m_A
总结
同名静态成员处理方式和非静态处理方式一样,只不过有两种访问方式
7、多继承语法
C++允许一个类继承多个类
语法:class 子类 : 继承方式 父类1, 继承方式 父类2 ...
多继承可能会引发父类中有同名成员出现,需要加作用域区分
如:父类1中存在属性A,而父类2中也存在属性A,这时候就需要通过加作用域来区分。
C++实际开发中不建议用多继承
总结:多继承中如果父类中出现了同名情况,子类使用时候要加作用域
8、菱形继承
(挖坑,暂时没听懂!)
概念:
两个派生继承同一个基类
又有某个类同时继承着两个派生类
这种继承被称为菱形继承,或者钻石继承
典型案例:
菱形继承为题:
1.羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
2.草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
解决方式:虚继承
在继承之前加上关键字virtual变为虚继承
如:
class Sheep : virtual public Animal {};
class Tuo : virtual public Animal {};
总结:
1.菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义。
2.利用虚继承可以解决菱形继承问题。