基类
-
定义基类
①.成员函数
用作基类的类通常会包含两种成员函数:一是希望派生类进行覆盖自己实现的函数,定义为虚函数;二是希望派生类直接继而不改变实现的函数。
②.虚析构函数
基类的析构函数必须是虚函数,为了多态时能执行动态绑定从而正确的释放内存。
③.受保护成员
使用关键字 protected 来声明受保护的成员。类似私有成员,类外不可访问;类似共有成员,派生类及友元可以访问;派生类从成员或者友元只能通过派生类对象访问基类受保护成员。
class Base { public: Base() = default; Base(string &s,int a):str(s),i(a) {}; virtual fucation();//虚成员函数 virtual ~Base();//虚析构函数 private: string str; protected: int i; };
-
抽象基类
①.纯虚函数
通过在函数体后加 =0 可以定义一个纯虚函数。一个纯虚函数无须定义其实现、但也可以为其定义实现,但必须是在类的外部定义。
②.抽象基类
包含有纯虚函数的类就是抽象基类,抽象基类不可以进行实例化操作。
class Base { public: Base() = default; Base(string &s,int a):str(s),i(a) {}; virtual void fucation() = 0;//纯虚函数 virtual ~Base();//虚析构函数 private: string str; protected: int i; }; Base b;//出错,不能实例化一个抽象类
派生类
-
派生类的定义
①.派生类的声明必须包含类派生列表
②.用作继承的基类必须是已定义的
class Derived:public Base { public: Derived() = default; Derived(string &s,int a,int b) :Base(s,a),j(b) {}; void fucation(); //覆盖基类中虚函数 private: int j; }
-
派生类中虚函数
①.virtual 非必须
一旦某个函数被定义成虚函数,则在所有派生类中都是虚函数,因此在派生类中可以不加 virtual 关键字。
②.形参类型及返回类型必须和基类保持一致
若派生类中虚函数的返回类型或者形参类型与基类不一致时,派生类中将会覆盖掉基类版本。
③.不更改派生中虚函数的参数缺省值
不应该重新定义一个继承而来的参数缺省值,因为参数缺省值是静态绑定,会得到和预期不一致的结果。
-
派生类对象
①.派生类对象构成
一个派生类对象可用认为有两部分构成:一是继承的基类对应的子对象;二是派生类自身定义的成员对象。
因为派生类中包含有基类对应的部分,因此把把派生类对象当作基类对象来使用,也可以使用基类的指针或者引用绑定到派生类对象的基类部分。
②.类型转换
基类的指针或者引用可用执行派生类对象,派生类向基类的隐式转换只针对指针或者引用有效;若基类的共用成员是可访问的,则派生类对象也可以转换成基类对象。
-
构造与析构函数
①.子类不继承基类构造函数
本质上子类会继承记录所有函数,包括构造函数,但是子类构造函数会覆盖基类的构造函数,看起来没有继承。若子类不定义任何构造函数,会默认调用父类的无参数构造函数。
class Base { public: Base(string &s):str(s) {}; virtual ~Base();//虚析构函数 private: string str; }; class Derived:public Base { }; D d;//出错,基类没有无参数构造函数,派生类也无无参数构造函数
②.析构函数必须是虚函数
非多态时,基类析构函数不是虚函数没有影响,可以正确释放内存。
多态时,基类析构函数不是虚函数,将会操作派生类部分内存没有正确释放。
③.派生类的构造函数只初始化它的直接基类
基类默认构造函数初始化派生类中基类部分,如果我们想拷贝或者移动基类部分,则必须在派生类的构造函数初始值列表中显式的使用基类的拷贝或者移动构造函数;派生类的赋值运算符也必须为其基类部分赋值。
多态
-
概念
①.静态类型:在编译时已知,是变量在声明时的类型。
②.动态类型:是变量表示的在内存中的对象的类型,直到运行时才可知。
③.多态:指向基类的指针或者引用直到程序运行时,才能确定指向的实际对象类型;多态就是根据实际的对象类型决定函数调用语句的具体调用目标。
-
虚函数表
①.虚函数表:虚函数表是一个存储成员函数指针的数据结构,是一个指针数组,每个元素对应一个虚函数的函数指针;虚函数表是属于类的,一个类一张表,由编译器在编译阶段自动生成和维护。
②.虚表指针:在每个类的对象内部包含一个虚表指针,指向自己所属类的虚表;对象在创建时,由编译器对vptr指针进行初始化,定义子类对象时,虚表指针先指向父类的虚函数表,在父类构造完成之后,子类的虚表指针才指向自己的虚函数表。
③.虚函数调用:对象的虚表指针用来指向自己所属类的虚表,虚表中的指针会指向其继承的最近的一个类的虚函数。