继承
继承的本质和原理
本质
- 代码的复用,基类提供所有派生类的公共函数接口,派生类也可以有自己特定的成员。
- 在基类中给所有派生类提供统一的虚函数接口,让所有派生类重写,方便实现多态
类与类之间的关系
- 组合:一部分的关系,一个类的对象是另一个类的成员
- 继承:一种的关系,一个类是另一个类的派生类。
继承方式
- 可见继承中,无论是什么继承方式基类的私有成员在派生类中都是不可访问的,并且访问权限是不能超越继承方式的权限的。
- 在继承中,基类的成员变量最好是使用protected,这样成员变量就可以在派生类中访问,并且在外边也是不可访问的。
代码示例
class Base
{
public :
Base(int data):ma(data)
{
cout << "Base()" << endl;
}
~Base() {
cout << "~Base()" << endl; }
protected:
int ma;
};
class Derive :public Base
{
public:
Derive(int data) :mb(data),Base(data)
{
cout << "Derive()" << endl;
}
~Derive() {
cout << "~Derive()" << endl; }
private:
int mb;
};
int main()
{
Derive m(2);
}
/*
代码结果
Base()
Derive()
~Derive()
~Base()
*/
派生类构造过程(代码可见)
- 派生类继承基类的所有成员和方法
- 派生类通过调用基类的构造函数,初始化基类的成员。
- 然后派生类调用自己的构造函数,初始化派生类的成员。
- 然后派生类首先调用自己的析构函数,释放对象。
- 派生类在调用基类的析构函数释放资源。
重载&隐藏&覆盖
代码示例
class Base
{
public:
Base(int data) :ma(data) {
}
void show() {
cout << "Base::show()" << endl; }
void show(int) {
cout << "Base::show(int)" << endl; }
protected:
int ma;
};
class Derive :public Base
{
public:
Derive(int data) :mb(data), Base(data) {
}
void show() {
cout << "Derive::show()" << endl; }
private:
int mb;
};
int main()
{
Derive m(2);
m.show(); // 调用派生类自己的show方法
// m.show(20);编译错误 派生类调用自己的show方法把基类的show方法隐藏掉了
// 调用基类的函数方法,需要添加作用域
m.Base::show();
m.Base::show(20);
}
由上面的代码给出重载,隐藏,覆盖的概念
重载关系
- 重载函数在统一作用域
- 如果派生类中没有基类的同名函数、则在基类中找同名方法
- 如果派生类中有基类的同名函数,就在派生类中找方法,隐藏掉基类的该函数
- 代码中基类的两个show方法。
隐藏关系
- 在继承关系中,派生类中的同名成员(变量和方法)把基类的同名成员覆盖了,其实是作用域隐藏,如果需要调用基类的成员,需要加基类作用域调用
- 派生类的show和基类的两个show,派生类覆盖基类的show。
覆盖(重写)
- 基类和派生类的函数方法的返回值,函数名,参数列表都相同且基类的函数方法实现成虚函数,然后派生类的函数方法就自动处理成虚函数,然后在派生类的虚函数表将基类继承的函数地址覆盖掉。
虚函数&虚函数表&虚函数指针
示例代码
class Base
{
public:
Base(int data) :ma(data) {
}
virtual void show() {
cout << "Base::show()" << endl; }
virtual void show(int) {
cout << "Base::show(int)" << endl; }
protected