2020/2/191第8章继承8.5多继承8.5.1多继承的派生类构造和访问8.5.2虚基类2020/2/1928.5多继承一个类有多个直接基类的继承关系称为多继承多继承声明语法class派生类名:访问控制基类名1,访问控制基类名2,…,访问控制基类名n{数据成员和成员函数声明};7.5多继承2020/2/1937.5多继承classC:publicA,publicBclassAclassB类C可以根据访问控制同时继承类A和类B的成员,并添加自己的成员7.5多继承2020/2/194多个基类的派生类构造函数用初始式调用基类构造函数,执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序。一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。如果不同的基类有同名成员,派生类对象访问时应该加以识别。8.5.1多继承的派生类构造和访问7.5.1多继承的派生类构造和访问2020/2/195classDerived:publicBase1,publicBase2{friendostream&operator(ostream&,constDerived&);public:Derived(int,char,double);doublegetReal()const;private:doublereal;};classBase1{public:Base1(intx){value=x;}intgetData()const{returnvalue;}protected:intvalue;};classBase2{public:Base2(charc){letter=c;}chargetData()const{returnletter;}protected:charletter;};多继承的简单应用(例8-11)7.5.1多继承的派生类构造和访问2020/2/196多继承的简单应用(例8-11)classBase1{public:Base1(intx){value=x;}intgetData()const{returnvalue;}protected:intvalue;};classBase2{public:Base2(charc){letter=c;}chargetData()const{returnletter;}protected:charletter;};classDerived:publicBase1,publicBase2{friendostream&operator(ostream&,constDerived&);public:Derived(int,char,double);doublegetReal()const;private:doublereal;};voidmain(){Base1b1(10);Base2b2('k');Derivedd(5,'A',2.5);:return;}'K'2.5'A'510valueletterrealBasc1b1Basc2b2Derivedd7.5.1多继承的派生类构造和访问2020/2/1978.5.2虚基类如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性7.5.2虚基类2020/2/198例如:classB{public:intb;};classB1:publicB{private:intb1;};classB2:publicB{private:intb2;};classC:publicB1,publicB2{public:intf();private:intd;};有:Cc;c.B;//errorc.B::b;//error,从哪里继承的?c.B1::b;//ok,从B1继承的c.B2::b;//ok,从B2继承的classC{f(),d}classB1{b1}classB2{b2}classB{b}classB{b}7.5.2虚基类2020/2/199例如:classB{public:intb;};classB1:publicB{private:intb1;};classB2:publicB{private:intb2;};classC:publicB1,publicB2{public:intf();private:intd;};classC{f(),d}classB1{b1}classB2{b2}classB{b}classB{b}#includeiostream.hvoidmain(){Cc;c.B1::b=5;c.B2::b=10;coutpathB1==c.B1::bendl;coutpathB2==c.B2::bendl;}7.5.2虚基类2020/2/1910例如:classB{public:intb;};classB1:publicB{private:intb1;};classB2:publicB{private:intb2;};classC:publicB1,publicB2{public:intf();private:intd;};classC{f(),d}classB1{b1}classB2{b2}classB{b}classB{b}c.bc.b1c.bc.b2c.dBBB1B2C多重派生类C的对象的存储结构示意建立C类的对象时,B的构造函数将被调用两次:一次由B1调用,另一次由B2调用,以初始化C类的对象中所包含的两个B类的子对象7.5.2虚基类2020/2/19118.5.2虚基类如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性如果在多条继承路径上有一个公共的基类,那么在继承路径的某处汇合点,这个公共基类就会在派生类的对象中产生多个基类子对象要使这个公共基类在派生类中只产生一个子对象,必须将这个基类声明为虚基类。虚基类声明使用关键字virtual7.5.2虚基类2020/2/1912例如:classB{public:intb;};classB1:virtualpublicB{private:intb1;};classB2:virtualpublicB{private:intb2;};classC:publicB1,publicB2{private:floatd;};有:Ccc;cc.b//okclassC{d}classB1{b1}classB2{b2}classB{b}由于类C的对象中只有一个B类子对象,名字b被约束到该子对象上,所以,当以不同路径使用名字b访问B类的子对象时,所访问的都是那个唯一的基类子对象。即cc.B1::b和cc.B2::b引用是同一个基类B的子对象7.5.2虚基类2020/2/19137.5.2虚基类例如:classB{public:intb;};classB1:virtualpublicB{private:intb1;};classB2:virtualpublicB{private:intb2;};classC:publicB1,publicB2{private:floatd;};classC{d}classB1{b1}classB2{b2}classB{b}带有虚基类的派生类C的对象的存储结构示意c.b1c.b2c.dc.bBB1B2C2020/2/1914#includeiostream.hclassA{public:A(){coutclassAendl;}};classB:publicA{public:B(){coutclassBendl;}};classC:publicA{public:C(){coutclassCendl;}};classD:publicB,publicC{public:D(){coutclassDendl;}};voidmain(){Ddd;}虚继承的测试classDclassBclassCclassAclassA两次调用A的构造函数7.5.2虚基类2020/2/1915#includeiostream.hclassA{public:A(){coutclassAendl;}};classB:publicA{public:B(){coutclassBendl;}};classC:publicA{public:C(){coutclassCendl;}};classD:publicB,publicC{public:D(){coutclassDendl;}};voidmain(){Ddd;}虚继承的测试classDclassBclassCclassAclassA:virtualpublicA:virtualpublicAclassDclassBclassCclassA一次调用A的构造函数7.5.2虚基类2020/2/1916继承是面向对象程序设计实现软件重用的重要方法。程序员可以在已有基类的基础上定义新的派生类。单继承的派生类只有一个基类。多继承的派生类有多个基类。派生类对基类成员的访问由继承方式和成员性质决定。创建派生类对象时,先调用基类构造函数初始化派生类中的基类成员。调用析构函数的次序和调用构造函数的次序相反。C++提供虚继承机制,防止类继承关系中成员访问的二义性。多继承提供了软件重用的强大功能,也增加了程序的复杂性。小结2020/2/1917第9章虚函数与多态性9.1静态联编9.2类指针的关系2020/2/1918多态性是指具有不同功能的函数可以用同一个函数名,这样就可以用同一个函数名调用不同内容的函数(如重载是静态多态性)。虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。2020/2/1919联编是指一个程序模块、代码之间互相关联的过程。静态联编,是程序的匹配、连接在编译阶段,也称为早期匹配。重载函数使用静态联编。动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编。switch语句和if语句是动态联编的例子。9.1静态联编2020/2/19209.1静态联编普通成员函数重载可表达为两种形式:1.在一个类说明中重载例如:Show(int,char);Show(char*,float);2020/2/19219.1静态联编普通成员函数重载可表达为两种形式:1.在一个类说明中重载例如:Show(int,char);与Show(char*,float);不是同一函数,编译能够区分2.基类的成员函数在派生类重载。有3种编译区分方法:(1)根据参数的特征加以区分2020/2/19229.1静态联编普通成员函数重载可表达为两种形式:1.在一个类说明中重载2.基类的成员函数在派生类重载。有3种编译区分方法:(1)根据参数的特征加以区分例如:A::Show();有别于B::Show();(2)使用“::”加以区分2020/2/19239.1静态联编普通成员函数重载可表达为两种形式:1.在一个类说明中重载2.基类的成员函数在派生类重载。有3种编译区分方法:(1)根据参数的特征加以区分(2)使用“::”加以区分例如:Aobj.Show()调用A::Show()Bobj.Show()调用B::Show()(3)根据类对象加以区分2020/2/1924基类指针和派生类指针与基类对象和派生类对象4种可能匹配:直接用基类指针引用基类对象;直接用派生类指针引用派生类对象;用基类指针引用一个派生类对象;用派生类指针引用一个基类对象。9.2类指针的关系2020/2/1925例如:A*p;//指向类型A的对象的指针AA_obj;//类型A的对象BB_obj;//类型B的对象p=&A_obj;//p指向类型A的对象p=&B_obj;//p指向类型B的对象,它是A的派生类利用p,可以通过B_obj访问所有从A_ob