1、1,C+面向对象程序设计,5.5 派生类的构造函数和析构函数,基类的构造函数不被继承,派生类中需要声明自己的构造函数。 在设计派生类的构造函数时候,不仅要考虑派生类所增加的数据成员初始化,也要考虑基类的数据成员初始化。 声明构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化,需要调用基类构造函数完成。 如果要调用基类带参数的构造函数,派生类的构造函数需为基类的构造函数传递参数。,5.5.1 简单的派生类的构造函数,简单的派生类 只有一个基类 只有一级派生 不包含基类的对象派生类构造函数名(总参数列表):基类构造函数名(参数列表) 派生类中新增数据成员初始化语句 ;,#in
2、clude /例5.5 #include using namespace std; class Student public:Student(int n, string nam, char s)num=n;namenam;sex=s;Student() protected:int num;string name;char sex; class Student1:public Student public:Student1(int n, string nam, char s,int a, string ad): Student(n,nam,s)age=a; addrad;,void show()
3、cout“num:“numendl;cout“name:“nameendl;cout“sex:“sexendl;cout“age:“ageendl;cout“address:“addrendl;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();st
4、ud2.show();return 0;,5.5.1 简单派生类的构造函数,5.5.1 简单的派生类的构造函数,建立一个对象时,执行构造函数的顺序是:派生类构造函数先调用基类的构造函数,再执行派生类构造函数本身(派生类构造函数的函数体)。,5.5.2 有子对象的派生类的构造函数,派生类构造函数的任务: 对基类数据成员初始化 对子对象数据成员初始化 对派生类数据成员初始化,#include /例5.6 #include class Student public:Student(int n, string nam)num=n; name=nam;void display()cout“num:“nu
5、mendl;cout“name:“nameendl; protected:int num;string name; ;,class Student1:public Student public: Student1(int n, string nam,int n1, string nam1,int a, string ad): Student(n,nam),monitor(n1,nam1) age=a; addr=ad; void show() cout“This student is:“endl;display();cout“age:“ageendl;cout“address:“addrend
6、l;void show_monitor() coutendl“Class monitor is:“ endl;monitor.display(); private:Student monitor;int age;string addr; ;,int main() Student1 stud1(10010,“Wang-li“, 1001,“Li-sun“,19,“115 Beijing Road,Shanghai“);stud1.show();stud1.show_monitor();return 0; ,5.5.2 有子对象的派生类的构造函数,5.5.2 有子对象的派生类的构造函数,派生类构造
7、函数一般形式: 派生类构造函数名(总参数表列):基类构造函数名1(参数表列1),基类构造函数名2(参数表列2),子对象名1(子对象参数1),子对象名2(子对象参数2), 派生类中新增数据成员初始化语句,5.5.2 有子对象的派生类的构造函数,执行派生类构造函数的顺序: 调用基类构造函数,对基类数据成员初始化 调用子对象构造函数,对子对象数据成员初始化 在执行派生类构造函数本身,对派生类数据成员初始化,class Student public:Student(int n, string nam )num=n; name=nam;void display()cout“num:“numendl; c
8、out“name:“nameendl;protected:int num; string name; class Student1:public Student public:Student1(int n, string nam, int a): Student(n,nam)age=a;void show() display();cout“age:“ageendl; private:int age;,5.5.3 多层派生时的构造函数,class Student2:public Student1 public:Student2(int n, string nam,int a,int s):Stu
9、dent1(n,nam,a)score=s;void show_all() show();cout“score:“scoreendl; private:int score; ; int main() Student2 stud(10010,“Li“,17,89);stud.show_all();return 0; ,5.5.3 多层派生时的构造函数,先初始化基类的数据成员num和name 在初始化Student1的数据成员age 最后再初始化Student2的数据成员score不要列出每一层派生类的构造函数,只要写出上一层派生类的构造函数即可,5.5.3 多层派生时的构造函数,5.5.4 派生
10、类构造函数的特殊形式,当不需要对派生类新增的成员进行任何初始化操作时,派生类构造函数的函数体可以为空,即构造函数是空函数 如果在基类中没有定义构造函数,或定义了没有参数的构造函数(默认构造函数),在定义派生类构造函数时可以不写基类构造函数。,5.5.4 派生类构造函数的特殊形式,如果在基类和子对象类型的声明中都没定义带有参数的构造函数,而且不需要对派生类自己的数据成员初始化,则可不必显式地定义派生类构造函数。 如果基类或子对象类型的声明中定义了带参数的构造函数,就必须显式地定义派生类构造函数。 如果基类中既定义无参的构造函数,又定义了有参的构造函数,在定义派生类构造函数时,既可以包含基类构造函
11、数及其参数,也可以不包含基类构造函数。,5.5.5 派生类的析构函数,析构函数也不被继承,派生类自行声明。 声明方法与一般(无继承关系时)类的析构函数相同。 不需要显式地调用基类的析构函数,系统会自动隐式调用。 析构函数的调用次序与构造函数相反。,派生类构造、析构函数的执行顺序,编译器调用构造函数的次序:编译器调用析构函数的次序:,基类,子对象,派生类,基类,子对象,派生类,5.6 多重继承,5.6.1 声明多重继承的方法,class 派生类名 :继承方式1 基类名1, 继承方式2 基类名2,派生类新增加的成员;,5.6.2 多重继承派生类的构造函数,派生类构造函数名(总参数表列):基类构造函
12、数名1(参数表列1),基类构造函数名2(参数表列2),子对象名1(子对象参数1),子对象名2(子对象参数2), 派生类中新增数据成员初始化语句,5.6.2 多重继承派生类的构造函数,构造函数的调用次序 调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左向右)。 调用成员对象的构造函数,调用顺序按照它们在类中声明的顺序。 派生类的构造函数体中的内容。,5.6.2 多重继承派生类的构造函数,#include #include using namespace std; class Teacherpublic:Teacher(string nam,int a,string t)name=nam
13、;age=a;title=t;void display()cout“name:“nameendl;cout“age“ageendl;cout“title:“titleendl;protected:string name;int age;string title; ;,class Student public:Student(string nam,char s,float sco)name1=nam;sex=s;score=sco; void display1() cout“name:“name1endl;cout“sex:“sexendl; cout“score:“scoreendl;prot
14、ected: string name1;char sex;float score; ;,5.6.2 多重继承派生类的构造函数,class Graduate:public Teacher,public Student 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:“nameendl;cout“age:“ageendl;cout“sex:“sexendl;cout“score
15、:“scoreendl;cout“title:“titleendl;cout“wages:“wageendl;private:float wage; ;,int main( )Graduate grad1(“Wang-li“,24,f,“assistant“,89.5,1234.5);grad1.show( );return 0; ,5.6.3 多继承引起的二义性问题,一般说来,在派生类中对基类成员的访问应该是惟一的。但是,由于多继承情况下,可能造成对基类中某个成员的访问出现了不惟一的情况,则称为对基类成员访问的二义性问题。,两个基类有同名成员,多重继承中,如果不同基类中有同名成员,在派生类中
16、就有同名的成员,这种成员会造成二义性。,A类int a;void display();,B类int a;void display();,C类int a;void display();int avoid display();int b;void show();,两个基类有同名成员,class A public:int a;void display(); ; class B public:int a;void display(); ;class C: public A, public B public:int b;void show(); ;,C c1; c1.a=3; /存在二义形 c1.dis
17、play(); /存在二义形可以用基类名来限定: c1.A:a=3; c1.A:display();,两个基类有同名成员,class A public:int a;void display(); ; class B public:int a;void display(); ;,class C: public A, public B public:int b;void show()a=3;/存在二义形dislpaly();/存在二义形 ; 可改为: class C: public A, public B public:int b;void show()A:a=3;A:dislpaly();,C类
18、int a;void display();int avoid display();int b;void show();,C类int b;int A:a;int B:a;void show();void A:display();void B:display();,A,B,C,两个基类有同名成员,基类和派生类有同名成员:派生类成员覆盖基类成员,class C: public A, public B int a;void display()couta“display of c”endl; ;void main() C c1;c1.display();,两个基类和派生类都有同名成员,c1.A:disp
19、lay();,A:aB:a,不同的成员函数,只有在函数名和参数个数相同、类型匹配的情况下才发生同名覆盖 如果只有函数名相同,不会发生同名覆盖,而属于函数重载,两个基类和派生类都有同名成员,访问共同基类成员,类A,类B1,类B2,类C,访问共同基类成员,访问共同基类成员,那么怎么通过类C访问类B1从基类A继承下来的成员呢?c1.B1:a=3;c1.a=3;和c1.A:a=3;都不正确 如果要通过类C访问类B2从基类A继承下来的成员c1.B2:a=3;,class A public:int a; ; class B1:public A public:int b1; ; class B2:publi
20、c A public:int b2; ; class C: public B1,public B2 public:int fun(); private:int c; ;,int C:fun() int sum(0);sum=a+a;sum=A:a+A:a;sum=B1:a+B2:a;return sum; void main() C c1;c1.a=3;c1.A:a=3;c1.B1:a=3;c1.B2:a=5; ,5.6.4 虚基类,虚基类的作用,当某类的部分或全部直接基类是从另一个共同基类派生而来时,这些直接基类中从上一级基类继承来的成员拥有相同的名称,即,这些同名成员在内存中存在多个副本,
21、虚基类的作用,类Cint B1:a;int B2:a; int B1:b1; int B2:b2;int c;,虚基类的作用,多数情况下,只需使用多个副本的任一个。 C+语言允许通过将直接基类的共同基类设置为虚基类,使从不同路径继承过来的该类成员在内存中只拥有一个副本,从而消除二义性问题。 引进虚基类的真正目的是为了解决二义性。 虚基类的说明在派生类的定义中。class 派生类名:virtual 继承方式 基类名,虚基类的作用,class A; class B1: virtual public A ;class B2: virtual public A ; Class C: public B1
22、, public B2 ;,虚基类的作用,类Cint a;int B1:b1; int B2:b2;int c;,虚基类的作用,一个基类可以在生成一个派生类时,作为虚基类,而在生成另一个派生类时不作为虚基类。,class A ; class B:virtual public A ; class C:virtual public A ; class D: public A ; class E:public B,public C ;,如果在虚基类中定义了带参数的构造函数,而没有定义默认构造函数,则在其所有派生类(包括直接派生或间接派生类)中,通过构造函数的初始化表对虚基类进行初始化。 以前,在派生
23、类的构造函数中只需调用其直接基类的构造函数。,虚基类的初始化,虚基类的初始化,class A A(int i); class B:virtual public A B(int n):A(n); class C:virtual public A C(int n):A(n);,class D: public A D(int n):A(n); class E:public B,public C E(int n):A(n),B(n),C(n);,#include class A public:A(int pa)a=pa;cout“A(int):“aendl; private: int a; ; cla
24、ss B1:virtual A public:B1(int pa,int pb1):A(pa)b1=pb1; cout“B1(int,int):”b1endl; private:int b1; ;,虚基类的初始化,class B2:virtual A public:B2(int pa, int pb2):A(pa)b2=pb2;cout“B2(int,int):“b2endl; private: int b2; ; class C:public B1, public B2 public:C(int p1, int p2, int p3, int p4):B1(p1, p2), B2(p2, p
25、3),A(p3)c=p4;cout“C(int, int, int, int):“cendl; private: int c; ; void main() C c1(1,2,3,4); ,虚函数的初始化,class Person /虚基类 public:Person(string nam,char s,int a) name=nam;sex=s;age=a;protected:string name; char sex; int age; ; class Teacher:virtual public Person public: Teacher(string nam,char s,int a,
26、 string t):Person(nam,s,a)title=t;protected:string title; ; class Student: virtual public Person public:Student(string nam,char s,int a,float sco):Person(nam,s,a),score(sco)protected: float score; ;,虚基类的简单应用举例,class Graduate:public Teacher,public Studentpublic:Graduate(string nam,char s,int a, strin
27、g 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:“nameendl “age:“ageendl“sex:“sexendl“score:“score endl“title:“titleendl“wages:“wageendl;private: float wage; ; int main( )Graduate grad1(“Wang-li“,f,24, “assistant“,89.5,1234.5);grad1.show( );return 0;,