C++函数继承
C++函数继承
引言
C++三大特征分别为封装,继承和多态,它们构成了面向对象编程的基石,它们协同工作以提升代码的模块化,可复用性和灵活性
封装:提高代码的维护性(当程序出现问题时可以准确定位)
继承:提高代码的复用性(在不做任何修改或者操作源码就能实现代码的复用)
多态:提高代码的扩展性(后期文章会介绍)
组合
C++中实现代码复用操作主要有两种,分别是组合和继承,二者各有优缺点
#include <iostream>using namespace std;class A{public:void func(){cout << "Hello world" << endl;}int m_num;};class B{public://创建A类成员A a;void func(){//此时B包含Aa.func();}};int main(){B b;b.func();return 0;}
执行结果:
上述代码就是一个典型的组合操作的实现,B包含A对象作为成员变量,这样B就可以调用A中的方法,此时修改A的内部逻辑并不会影响B(只要接口不变),操作时也更加直接,可以组合多个类,操作方法都是通过成员对象来访问,这样就在不复制代码内容的情况下使用了其他类中的功能。但这种方法每次组合一个类就要多创建一个成员,此时会增大内存占用。
继承
继承同样在代码复用方面起着重要作用
#include <iostream>using namespace std;class A{public:void print(){cout << "Hello world" << endl;}int m_num;};class B : public A{public://继承之后无需声明A类成员便可直接调用A类内的共有函数void func(){print();}};int main(){A a;B b;b.func();//继承了A类之后便可以直接使用与A相同的成员b.m_num = 10;b.print();cout << b.m_num << endl;cout << a.m_num << endl;cout << sizeof(b) << endl;return 0;}
输出结果
在这里与组合相比,继承就可以减少内存开销,继承之后B称为A的子类或派生类,A称为B的父类。派生类的实例化对象大小:父类的对象大小 + 派生类的新成员;与此同时我们还应该注意继承的空间分配问题,继承后派生类和父类并不公用内存,派生类会在继承后在自己的内存空间内开辟新的内存来存放与父类不同的成员,大致原理如下
根据程序输出结果也不难看出修改了B类成员后A类成员并没有一同发生改变
覆盖
#include <iostream>using namespace std;class A{public:void print(){cout << "Hello world" << endl;}int m_num = 12;};class B : public A{public://与父类重名的成员会在派生类中重新创建int m_num;int count;// 继承之后无需声明A类成员便可直接调用A类内的共有函数void func(){print();}//与父类重名的函数会发生覆盖void print(){cout << "Hi world" << endl;}};int main(){A a;B b;b.func();b.m_num = 10;b.print();cout << sizeof(b) << endl;cout << b.m_num << endl;cout << a.m_num << endl;return 0;}
输出结果
根据输出结果不难推理出派生类中的重名函数以及重名成员会覆盖子类中的对应函数和成员
三种继承方式
公有继承
基类的公有成员和属性成为派生类的共有;基类的被保护的属性和方法成为派生类的被保护;基类私有成员不能被继承
保护继承
基类的公有成员和属性成为派生类的私有;基类的被保护的属性和方法成为派生类的私有;基类私有成员不能被继承
私有继承
基类的公有成员和属性成为派生类的被保护的;基类的被保护的属性和方法成为派生类的被保护的;基类私有成员不能被继承
三种继承方式操作方法基本相同,只是权限控制不同,由于我们使用继承的出发点是实现代码复用,所以三种继承方式中使用最多的就是公有继承
组合和继承的选择
当两者之间是has-a(例如:学生有书包,汽车有发动机)关系时通常使用组合方式,当两者关系为is-a关系(例如:老师是人,学生也是人)时使用继承方式