继承
通过继承联系在一起的类构成一种层次关系。通常在层次关系的根部有一个基类,其他类是从基类继承而来,这些通过继承得到的类叫做派生类 。
举个例子
class Base{};class Derived:public Base{};
上面出现的两个类中,Base称为基类,Derived称为派生类。表示继承关系时可以按照以下方式:
class 派生类:访问说明符 基类{/* … */};
其中访问说明符可以分为三种:public,protected,private。
public权限继承:派生类以public权限继承基类时,基类的成员访问属性在派生类中没有变化。在类外可以访问到基类中public成员。
protected权限继承:派生类以protect权限继承基类时,基类的public成员在派生类中的访问属性是protected。在类外不能访问到基类中的成员。
private权限继承:派生类以private权限继承基类时,基类的public与protected成员在派生类中的访问属性是private。在类外不能访问到基类中的成员。
注意!派生访问说明符对于派生类成员访问基类的成员没什么影响,对于基类的访问权限只与基类中的访问说明符有关。也就是说,无论派生类以public权限或者private权限继承基类,都可以访问到基类的public成员和protected成员。
空间模型
继承与构造函数&析构函数
我们知道,如果用户没有显示定义类的构造函数,编译器会自动合成默认的构造函数。那么编译器什么时候会合成构造函数呢?
1. 基类中没有缺省构造函数,那么派生类必须合成构造函数。
2. 基类中没有显示定义构造函数,那么派生类不会合成构造函数。
3. 基类中给出了带有形参表的构造函数,那么派生类必须合成构造函数。
构造函数的调用顺序:首先调用派生类的构造函数,但是执行构造函数的顺序是从基类开始到派生类。我们去vs编译器下测试一下。
#include<iostream>using namespace std;class B{public: B() { cout<<"B构造"<<endl; } ~B() { cout<<"B析构"<<endl; }};class D:public B{public: D(int d) { cout<<"D构造"<<endl; } ~D() { cout<<"D析构"<<endl; }};int main(){ D d(10); return 0;}
定义了一个基类B,定义了一个派生类D以public权限继承B。“D d(10);“这条语句的反汇编如下
我们看到call D::D这样的语句,这就是编译器调用了D的构造函数。
析构函数的调用顺序:从派生类到基类的顺序调用。
让我们看看运行结果,来验证一下。
调用顺序正如同所说。
同名隐藏问题
什么是同名隐藏?
同名隐藏就是在继承关系中,不同作用域(基类和派生类)的相同成员变量或者同名函数在被访问时,派生类会屏蔽基类对成员的直接访问。
如何解决同名隐藏问题?
1. 在成员前面标明类域。比如派生类基类(Base)中存在同名变量_n,若要访问基类的_n,需要标明_n的类域Base::_n进行访问。
2. 为了防止出现同名隐藏,尽量不要定义同名变量或者函数。
赋值兼容规则
1. 派生类可以赋值给基类,基类不可以赋值给派生类。
2. 基类指针可以指向派生类,派生类指针不能指向基类(可以通过强制类型转换完成,不过不建议)。
继承与友元
友元关系不能继承,也就是说基类的友元不能够访问派生类的私有和保护成员。
继承与静态成员
基类定义了一个static成员,那么整个继承体系中只存在一个这样的成员。