💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 概念 * 在C++中可重用性是通过继承(inheritance)这一机制来实现的。 * “继承”就是在一个已存在的类的基础上建立一个新的类 * 一个新类从已有的类那里获得其已有特性,这种现象称为类的继承。 * 通过继承,派生类继承了基类的所有数据成员和绝大多数成员函数(构造函数和析构函数除外),并可以对成员作必要的增加或调整。 * 一个基类可以派生出多个派生类,每一个派生类又可以作为基类再派生出新的派生类,因此基类和派生类是相对而言的。 * 一个派生类有两个或多个基类的称为多重继承(multiple inheritance)。 * 派生类是基类的具体化,而基类则是派生类的抽象。 # **派生** ## **声明方式** ~~~ class 派生类名: [继承方式] 基类名 { 派生类新增加的成员 } ; ~~~ ### **继承方式** * 继承方式包括: public(公用的),private(私有的)和protected(受保护的) * 如果不写此项,则默认为private(私有的)。 ![image-20200607004332556](file://C:/Users/28165/AppData/Roaming/Typora/typora-user-images/image-20200607004332556.png?lastModify=1591534314) ## **构成** * 基类中继承来的数据成员和成员函数 * 新增的数据成员和成员函数 * 构造函数和析构函数不能继承,所以需要自己重新写 ## 构造函数 * 派生类所增加的数据成员的初始化 * 基类的数据成员初始化 * 使用冒号语法,对基类数据成员进行初始化 ### 顺序 ① 首先,调用基类构造函数,对基类数据成员初始化; ② 其次,调用子对象构造函数,对对象成员初始化; ③ 最后,执行派生类构造函数本身,对派生类普通数据成员初始化. ### 形式 ~~~ 派生类构造函数名(总参数表列): 基类构造函数名(参数表列),对象成员名(参数表列) {    //派生类中新增数据成员的初始化语句 } ~~~ ### 例子 ~~~ #include <iostream> #include<string> using namespace std; ​ class Student //声明基类Student {    public:                                         Student(int n,string nam,char s)           //基类构造函数   {       num=n;       name=nam;       sex=s;   }   ~Student( ){ } //基类析构函数    protected:                            //保护部分   int num;        string name;   char sex ;                             }; class Student1: public Student     //声明派生类Student1 {    public:                           //派生类的公用部分   Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s) //派生类构造函数   {       age=a;               //在函数体中只对派生类新增的数据成员初始化       addr=ad;   }   void show( )   {       cout<<″num: ″<<num<<endl;       cout<<″name: ″<<name<<endl;       cout<<″sex: ″<<sex<<endl;       cout<<″age: ″<<age<<endl;       cout<<″address: ″<<addr<<endl<<endl;   }   ~Student1( ){ } //派生类析构函数    private: //派生类的私有部分   int age;                             string addr;                 }; ​ int main( ) {    Student1 stud1(10010,″Wang-li″,′f′,19,″115 Beijing Road,Shanghai″);    Student1 stud2(10011,″Zhang-fun″,′m′,21,″213 Shanghai Road,Beijing″);    stud1.show( ); //输出第一个学生的数据    stud2.show( ); //输出第二个学生的数据    return 0; } ~~~ # 多重继承 一个派生类同时继承多个基类,称为多重继承。 ## 形式 ~~~ class D:public A,private B,protected C {    //类D新增加的成员 } ​ //构造函数 派生类构造函数名(总参数表列): 基类1构造函数(参数表列), 基类2构造函数(参数表列), 基类3构造函数 (参数表列) {    //派生类中新增数成员据成员初始化语句 } ​ ​ ~~~ ## 例子 ~~~ #include <iostream> #include <string> #include <cstring> using namespace std; class Teacher //声明类Teacher(教师) {    public: //公用部分    Teacher(string nam,int a, string t)      //构造函数   {        name=nam;     age=a;     title=t;   }    void display( ) //输出教师有关数据   {        cout<<″name:″<<name<<endl;        cout<<″age″<<age<<endl;        cout<<″title:″<<title<<endl;   }    protected: //保护部分    string name;    int age;    string title; //职称 }; class Student //定义类Student(学生) {    public:    Student(char nam[],char s,float sco)   {        strcpy(name1,nam);     sex=s;     score=sco;   } //构造函数    void display1( ) //输出学生有关数据   {        cout<<″name:″<<name1<<endl;        cout<<″sex:″<<sex<<endl;        cout<<″score:″<<score<<endl;   }    protected:                               //保护部分    string name1;    char sex;    float score;                            //成绩 }; class Graduate:public Teacher,public Student  //声明多重继承的派生类Graduate {    public:    Graduate(string nam,int a,char s, string t,float sco,float w)       :Teacher(nam,a,t),Student(nam,s,sco),wage(w) { }    void show( ) //输出研究生的有关数据   {        cout<<″name:″<<name<<endl;        cout<<″age:″<<age<<endl;        cout<<″sex:″<<sex<<endl;        cout<<″score:″<<score<<endl;        cout<<″title:″<<title<<endl;        cout<<″wages:″<<wage<<endl;   }    private:    float wage;                     //工资 }; int main( ) {    Graduate grad1(″Wang-li″,24,′f′,″assistant″,89.5,1234.5);    grad1.show( );    return 0; } ~~~ ## 二义性 当基类与派生类之间出现相同的数据成员时,就有二义性 ### 解决办法 ~~~ // 指明作用域 //Student和Teacher类中有相同的name数据时 cout<<″name:″<<Teacher::name<<endl;   //在Student类中调用Teacher类的name ~~~ ### 同名覆盖 * 基类的同名成员在派生类中被屏蔽,成为“不可见”的 * 派生类新增加的同名成员覆盖了基类中的同名成员。 * 不同的成员函数,只有在函数名和参数个数相同、类型相匹配的情况下才发生同名覆盖,如果只有函数名相同而参数不同,不会发生同名覆盖,而属于函数重载。 # 虚基类 * 使得在继承间接共同基类时只保留一份成员。 * 虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的 * 在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化。 * C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类(如类B和类C) 对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。 ## 形式 ~~~ class A //声明基类A {…}; class B :virtual public A //声明类B是类A的公用派生类,A是B的虚基类 {…}; class C :virtual public A //声明类C是类A的公用派生类,A是C的虚基类 {…}; ~~~ ## 例子 ~~~ #include <iostream> #include <string> using namespace std; ​ //声明公共基类Person class Person {    public:    Person(string nam,char s,int a)//构造函数   {        name=nam;sex=s;age=a;   }    protected:                                        //保护成员    string name;    char sex;    int age; }; //声明Person的直接派生类Teacher class Teacher:virtual public Person   //声明Person为公用继承的虚基类 {    public:                                  Teacher(string nam,char s,int a, string t)       :Person(nam,s,a) //构造函数       {            title=t;       }    protected:              //保护成员    string title;           //职称 }; ​ //声明Person的直接派生类Student class Student:virtual public Person     //声明Person为公用继承的虚基类 {    public:    Student(string nam,char s,int a,float sco) //构造函数       :Person(nam,s,a),score(sco){ } //初始化表    protected:                                       //保护成员    float score;                                 //成绩 }; ​ //声明多重继承的派生类Graduate class Graduate:public Teacher,public Student   //Teacher和Student为直接基类 {    public:    Graduate(string nam,char s,int a, string t,float sco,float w) //构造函数       :Person(nam,s,a),Teacher(nam,s,a,t),Student(nam,s,a,sco),wage(w){}//初始化表    void show( ) //输出研究生的有关数据   {        cout<<″name:″<<name<<endl;        cout<<″age:″<<age<<endl;        cout<<″sex:″<<sex<<endl;        cout<<″score:″<<score<<endl;        cout<<″title:″<<title<<endl;        cout<<″wages:″<<wage<<endl;   }    private:    float wage;                     //工资 }; int main( ) //主函数 {    Graduate grad1(″Wang-li″,′f′,24,″assistant″,89.5,1234.5);    grad1.show( );    return 0; } ~~~ # 继承与组合 * 在一个类中以某类对象作为数据成员的,称为类的组合(composition)。 * 继承是纵向的,组合是横向的。