C++大学基础教程 第9章 继承与派生 北京科技大学
第9章 继承与派生 9.1 继承的概念 9.2 继承方式 9.3 派生类构造函数的定义 9.4 多继承
第9章 继承与派生 类具有封装性、继承性和多态性
第9章 继承与派生 主要介绍: 继承和派生的概念 继承方式; 派生类的构造函数与析构函数; 多继承中的二义性 虚基类。
9.1 继承的概念 类的继承是在现有类的基础之上,创建新类的机制。称现有的类为基类,新建立的类为派生类。 不必从开始创建特殊的程序对象 新类继承了基类的属性和行为 新类是基类的特殊情况。 不必从开始创建特殊的程序对象 继承是处理“特殊情况”的面向对象编程机制
派生类的定义格式 派生类只有 一个直接基 类为单继承 class 派生类名:继承方式 基类名 { public: //派生类公有成员… private: //派生类私有成员… }
例: 定义基类shape class shape { private: int m_x,m_y; //位置 char m_color; //颜色 public: void setposition(int x, int y); void setcolor(char color); int getx(); int gety(); char getcolor(); };
定义派生类(等边三角形类) class Triangle: public Shape { public: Triangle(int x, int y, char color='R', float slen = 1); float GetSideLength() const; void SetTriangle(int x, int y, char color, float slen); void Draw(); private: float m_SideLength; };
派生新类: circle 圆形 rectangle 矩形 triangle 三角形 shape 基类称为父类 派生类称为子类 circle
派生类的定义格式 class 派生类名:继承方式 基类名1, … 继承方式 基类名n { public: //派生类公有成员… 有多个基类 class 派生类名:继承方式 基类名1, … 继承方式 基类名n { public: //派生类公有成员… private: //派生类私有成员… } 派生类有多个基类为多继承
例:已有基类base1,base2,base3,定义派生类deriver class deriver:public base1,public base2,private base3 { private: int m_derdata; public: void derfunction(); };
注意 每一个“继承方式”,只用于限制对紧随其后之基类的继承。 类的继承方式是派生类对基类成员的继承方式。 类的继承方式指定了类外对象对于派生类从基类继承来的成员的访问权限。
直接基类和间接基类 class base {…… }; class deriver1:public base { …… class deriver2:public deriver1 } 父类被称为子类的直接基类 父类的父类或更高层次的父类被称为这个子类的间接基类
派生与继承的实例 公司人员管理系统: 小型公司人员分为: 经理、兼职技术人员、销售经理和兼职推销员. 要求: 存储所有人员的姓名、编号、级别、当月薪水,计算月薪总额并显示全部信息。 人员编号在生成人员信息时同时生成,每输入一个人员信息编号顺序加1。 程序能够对不同人员按不同方法提升级别,月薪的计算方法是: 经理拿固定月薪; 兼职技术人员按工作小时数领取月薪; 兼职推销员的报酬按该推销员当月销售额提成; 销售经理既拿固定月薪也领取销售提成。
派生与继承的实例 分析: ·描述全体职员的共性(基类) ·描述每一类特殊人员(派生类)
class employee { protected: char *name; //姓名 int individualEmpNo; //个人编号 int grade; //级别 float accumPay; //月薪总额 static int employeeNo; //本公司职员编号目前最大值 public: employee(); //构造函数 ~employee(); //析构函数 void pay(); //计算月薪函数 void promote(int); //升级函数 void displayStatus(); //显示人员信息 };
class technician: public employee //兼职技术人员类 { private: float hourlyRate; //每小时酬金 int workHours; //当月工作时数 public: technician(); //构造函数 void pay(); //计算月薪函数 void displayStatus(); //显示人员信息 }; 新增加的成员 同名覆盖,改造基类成员 派生类的成员: 1. 从基类继承的成员; 2. 改造基类成员; 3. 添加派生类新成员.
9.2 继承方式 三种继承方式 public、protected、private 不同继承方式的影响主要体现在: 派生类 成员 对基类成员的访问控制。 派生类 对象 对基类成员的访问控制。 定义派生类时要声明继承方式
9.2.1. 派生类的定义 派生类的定义形式: class 派生类名:继承方式 基类1, 继承方式 基类2,…,继承方式 基类n { 派生类成员声明; };
例如: 设已有基类base1和base2,定义派生类deriver. class deriver: public base1,private base2 { private: int newmember; public: void newfun(); };
单继承情况,派生类的定义 class 派生类名:继承方式 基类名 { 派生类成员声明 };
例9.1 图形类及其派生类的声明 class Shape { public: Shape(int x=0, int y=0, char c = 'R'); int GetX() const; void SetX( int x); int GetY() const; void SetY( int x); char GetColor() const; void SetColor(char c); protected: char m_color; int m_x; int m_y; };
class Circle : public Shape { public: Circle(int x, int y, float r=1, char color='R'); float GetRadius () const; void SetCircle(int x, int y, float r, char color); void Draw(); private: float m_Radius; };
class Triangle: public Shape { public: Triangle(int x, int y, char color='R', float slen = 1); float GetSideLength() const; void SetTriangle(int x, int y, char color, float slen); void Draw(); private: float m_SideLength; };
class Rectangle: public Shape { public: Rectangle(int x, int y, char color, int length=10, int width=10); int GetWidth() const; int GetHeight() const; void Draw(); void SetRectangle (int x, int y, char color, int length, int width); private: int m_Width; int m_Length; };
派生类的成员包括:(1)继承基类的成员,(2)派生类定义时声明的成员。 派生类自己增加的成员,完成两个需求:(1)修改基类成员,(2)描述新的特征或方法。 m_color; m_x; m_y; GetX();SetX(); GetY();SetY(); GetColor(); SetColor(); m_Radius; GetRadius () SetCircle(); Draw(); m_color; m_x; m_y; GetX();SetX(); GetY();SetY(); GetColor(); SetColor(); m_SideLength; GetSideLength(); SetTriangle(); Draw(); m_color; m_x; m_y; GetX();SetX(); GetY();SetY(); GetColor(); SetColor(); m_Width; m_Length; GetWidth(); GetHeight(); Draw(); SetRectangle(); 从基类 继承的 成员 派生类 增加的 成员
同名覆盖 派生类修改基类的成员,是在派生类中声明了一个与基类成员同名的新成员。在派生类作用域内或者在类外通过派生类的对象直接使用这个成员名,只能访问到派生类中声明的同名新成员,这个新成员覆盖了从基类继承的同名成员,这种情况称为同名覆盖。
class Shape { public: ┇ void Draw(){}; protected: }; class Triangle: public Shape { public: Triangle(int x, int y, char color='R', float slen = 1); float GetSideLength() const; void SetTriangle(int x, int y, char color, float slen); void Draw(); private: float m_SideLength; };
例9.2 同名覆盖示例 #include<iostream> using namespace std; class base { public: void function(){cout<<"function of class base"<<endl;} }; class deriver: public base void function(){cout<<"function of class deriver"<<endl;}
void main() { deriver derobj; derobj.function(); } 输出结果: function of class deriver
例9.3 派生类成员函数对基类同名函数的改进。 //number.h class Number { protected: int m_number; public: int GetNumber(){ return m_number; } void SetNumber(int n){ m_number=n;} void Prime(); };
//number.cpp #include<iostream> #include "number.h" using namespace std; void Number::Prime() { int i; for(i=2; i<m_number; i++) //找m_number的因数 { if(m_number %i==0) break; } if(m_number ==i) //判断m_number是否被小于m_number的数整除 cout <<m_number <<" is prime"<<endl; else cout <<m_number <<" isn't prime"<<endl; }
//DerNumber.h class DerNumber: public Number { public: void Prime(); };
//DerNumber.cpp #include<iostream> #include<cmath> #include "number.h" #include "DerNumber.h" using namespace std; void DerNumber::Prime() { double sqrtm=sqrt(m_number); //用到math.h int i; for(i=2; i<=sqrtm; i++) { if(m_number %i==0) break; } if(sqrtm<i) cout <<m_number <<" is prime.\n"; else cout <<m_number <<" isn't prime.\n"; }
//使用模块 #include<iostream> #include<ctime> #include "number.h" #include "DerNumber.h" using namespace std; void main() { Number aNum; DerNumber aDerNum; clock_t start, finish;
double elapsed_time1,elapsed_time2; int i; unsigned int max(100000); time( &start ); for(i=10000;i<=max;i++) { aNum.SetNumber(i); cout<<aNum.GetNumber()<<" "<<endl; aNum.Prime(); } time( &finish );
elapsed_time1 = difftime( finish, start ); time( &start ); for(i=10000;i<=max;i++) { aDerNum.SetNumber(i); cout<<aDerNum.GetNumber()<<" "<<endl; aDerNum.Prime(); } time( &finish ); elapsed_time2 = difftime( finish, start );
cout<<"Delay for using Number class: "<<elapsed_time1 <<" seconds"<<endl; cout<<"Delay for using DerNumber class: " <<elapsed_time2<<" seconds"<<endl; } 输出结果(部分): Delay for using Number class: 157 seconds Delay for using DerNumber class: 151 seconds
9.2.2. 继承的访问控制 派生类继承了基类中除构造函数和析构函数之外的所有成员。
9.2.2. 继承的访问控制 三种继承方式: 公有继承(public) 私有继承(private) 保护继承(protected)
9.2.2. 继承的访问控制 不同的继承方式使得派生类从基类继承的成员具有不同的访问控制权限,以实现数据的安全性和共享性控制。 不同继承方式决定的不同访问控制权限体现在: 派生类的成员函数对其继承的基类成员的访问控制; 其它模块通过派生类对象对其继承的基类成员的访问控制。
1. 公有继承 公有继承的派生类定义形式: class 派生类名:public 基类名 { 派生类新成员定义; }
公有继承 public是定义公有继承方式的关键字 公有继承方式定义的派生类,继承了基类中除构造函数和析构函数外的其余成员:公有成员、保护成员和私有成员 被继承的基类成员在派生类中仍将保持其原来的访问属性。 派生类的成员函数可以访问基类的公有成员和保护成员,不能访问基类的私有成员; 派生类以外的其它函数可以通过派生类的对象,访问从基类继承的公有成员, 但不能访问从基类继承的保护成员和私有成员。
class Point //基类Point类的定义 { public: //公有函数成员 void InitP(float xx=0, float yy=0) {X=xx;Y=yy;} void Move(float xOff, float yOff) {X+=xOff;Y+=yOff;} float GetX() {return X;} float GetY() {return Y;} private: //私有数据成员 float X,Y; };
派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。 class Rectangle: public Point //派生类声明部分 { public: //新增公有函数成员 void InitR(float x, float y, float w, float h) { InitP(x,y); //访问基类公有成员函数 W=w;H=h;} float GetH() {return H;} float GetW() {return W;} private: //新增私有数据成员 float W,H; }; 派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。
使用派生类的 对象 只能访问基类的public成员 main() { Rectangle rect; cout<<rect.X; //? cout<<rect.GetX();//?? } 使用派生类的 对象 只能访问基类的public成员
class Rectangle: public Point //派生类声明部分 { public: //新增公有函数成员 void InitR(float x, float y, float w, float h) {X=x; Y=y; //?访问基类私有成员 W=w;H=h;} float GetH() {return H;} float GetW() {return W;} private: //新增私有数据成员 float W,H; }; 派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。
class Point //基类Point类的定义 { public: void InitP(float xx=0, float yy=0) {X=xx;Y=yy;} void Move(float xOff, float yOff) {X+=xOff;Y+=yOff;} float GetX() {return X;} float GetY() {return Y;} protected: float X,Y; };
class Rectangle: public Point //派生类声明部分 { public: //新增公有函数成员 void InitR(float x, float y, float w, float h) {X=x; Y=y; //??访问基类的保护成员 W=w;H=h;} float GetH() {return H;} float GetW() {return W;} private: //新增私有数据成员 float W,H; }; 派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。
依然错误! 使用派生类的 对象 只能访问基类的public成员 main() { Rectangle rect; cout<<rect.X; //? cout<<rect.GetX();//?? } 使用派生类的 对象 只能访问基类的public成员 依然错误!
2.私有继承 私有继承的派生类定义形式: class 派生类名:private 基类名 { 派生类新成员定义; }
私有继承 private是定义私有继承方式的关键字 以私有继承方式定义的派生类,继承了基类中可以继承的成员:公有成员、保护成员和私有成员,这些成员在派生类中的访问属性都是私有的。 派生类的成员函数可以访问基类的公有成员和保护成员,不能访问基类的私有成员。 派生类以外的其它函数则不能通过派生类的对象访问从基类继承的任何成员。
class Point //基类声明 { public: void InitP(float xx=0, float yy=0) {X=xx;Y=yy;} void Move(float xOff, float yOff) {X+=xOff;Y+=yOff;} float GetX() {return X;} float GetY() {return Y;} private: float X,Y; };
class Rectangle: private Point //派生类声明 {public: //新增外部接口 void InitR(float x, float y, float w, float h) {InitP(x,y);W=w;H=h;} //派生类访问基类公有成员 void Move(float xOff, float yOff) {Point::Move(xOff,yOff);} float GetX() {return Point::GetX();} float GetY() {return Point::GetY();} float GetH() {return H;} float GetW() {return W;} private: //新增私有数据 float W,H; }; 派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。
派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。 class Rectangle: private Point //派生类声明 {public: //新增外部接口 void InitR(float x, float y, float w, float h) { X=x; Y=y;//? W=w;H=h; } void Move(float xOff, float yOff) {Point::Move(xOff,yOff);} float GetX() {return Point::GetX();} float GetY() {return Point::GetY();} float GetH() {return H;} float GetW() {return W;} private: //新增私有数据 float W,H; }; 派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。
class Point //基类声明 { public: void InitP(float xx=0, float yy=0) {X=xx;Y=yy;} void Move(float xOff, float yOff) {X+=xOff;Y+=yOff;} float GetX() {return X;} float GetY() {return Y;} protected: float X,Y; };
class Rectangle: private Point //派生类声明 {public: //新增外部接口 void InitR(float x, float y, float w, float h) { X=x; Y=y;//?? W=w;H=h; } void Move(float xOff, float yOff) {Point::Move(xOff,yOff);} float GetX() {return Point::GetX();} float GetY() {return Point::GetY();} float GetH() {return H;} float GetW() {return W;} private: //新增私有数据 float W,H; };
错误! 使用派生类的 对象 不能访问基类中的任何成员。 main() { Rectangle rect; cout<<rect.X; //? cout<<rect.GetX();//? } 使用派生类的 对象 不能访问基类中的任何成员。 错误!
3. 保护继承 保护继承的派生类定义形式: class 派生类名:protected 基类名 { 派生类新成员定义; }
保护继承 protected是定义保护继承方式的关键字 以保护继承方式定义的派生类,继承了基类中可以继承的成员:公有成员、保护成员和私有成员。其中基类的公有成员和保护成员在派生类中访问控制属性变成保护类型的,基类的私有成员保持原来属性。 派生类的成员函数可以访问基类的公有成员和保护成员,不能访问基类的私有成员。 派生类以外的其它函数则不能通过派生类的对象访问从基类继承的任何成员。
class Point //基类声明 { public: void InitP(float xx=0, float yy=0) {X=xx;Y=yy;} void Move(float xOff, float yOff) {X+=xOff;Y+=yOff;} float GetX() {return X;} float GetY() {return Y;} private: float X,Y; };
class Rectangle: protected Point //派生类声明 {public: //新增外部接口 void InitR(float x, float y, float w, float h) {InitP(x,y);W=w;H=h;} //派生类访问基类公有成员 void Move(float xOff, float yOff) {Point::Move(xOff,yOff);} float GetX() {return Point::GetX();} float GetY() {return Point::GetY();} float GetH() {return H;} float GetW() {return W;} private: //新增私有数据 float W,H; }; 派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。
派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。 class Rectangle: protected Point //派生类声明 {public: //新增外部接口 void InitR(float x, float y, float w, float h) { X=x; Y=y;//? W=w;H=h; } void Move(float xOff, float yOff) {Point::Move(xOff,yOff);} float GetX() {return Point::GetX();} float GetY() {return Point::GetY();} float GetH() {return H;} float GetW() {return W;} private: //新增私有数据 float W,H; }; 派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。
class Point //基类声明 { public: void InitP(float xx=0, float yy=0) {X=xx;Y=yy;} void Move(float xOff, float yOff) {X+=xOff;Y+=yOff;} float GetX() {return X;} float GetY() {return Y;} protected: float X,Y; };
派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。 class Rectangle: protected Point //派生类声明 {public: //新增外部接口 void InitR(float x, float y, float w, float h) { X=x; Y=y;//?? W=w;H=h; } void Move(float xOff, float yOff) {Point::Move(xOff,yOff);} float GetX() {return Point::GetX();} float GetY() {return Point::GetY();} float GetH() {return H;} float GetW() {return W;} private: //新增私有数据 float W,H; }; 派生类中的 成员函数 可以直接访问基类中的public和protected成员,但不能访问基类的private成员。
错误! main() { Rectangle rect; cout<<rect.X; //? cout<<rect.GetX();//? } 使用派生类的 对象 不能访问基类中的任何成员。 错误!
9.3 派生类构造函数的定义 派生类继承了基类中除构造函数和析构函数之外的所有成员。 基类的构造函数和析构函数不能被派生类所继承,派生类需要自己定义的构造函数和析构函数。
9.3.2 派生类的构造函数 派生类构造函数的一般形式: 派生类名::派生类名(基类所需的形参,本类成员所需的形参): 9.3.2 派生类的构造函数 派生类构造函数的一般形式: 派生类名::派生类名(基类所需的形参,本类成员所需的形参): 基类1(基类参数表1), …,基类n(基类参数表n), 对象成员1(对象参数表1), …,对象成员m(对象参数表m) { 本类基本类型数据成员初始化; }
派生类的构造函数 单继承时的构造函数 派生类名::派生类名(基类所需的形参,本类成员所需的形参):基类名(参数) { 本类成员初始化赋值语句; }
class Point //基类Point类的定义 { public: //公有函数成员 Point(int xx=0, int yy=0) {X=xx;Y=yy;} void InitP(int xx=0, int yy=0) {X=xx;Y=yy;} void Move(int xOff, int yOff) {X+=xOff;Y+=yOff;} int GetX() {return X;} int GetY() {return Y;} private: //私有数据成员 int X,Y; };
class Rectangle: public Point { public: Rectangle(int x, int y, int w, int h); void InitR(int x, int y, int w, int h) {InitP(x,y);W=w;H=h;} //派生类访问基类公有成员 void Move(int xOff, int yOff) {Point::Move(xOff,yOff);} int GetX() {return Point::GetX();} int GetY() {return Point::GetY();} int GetH() {return H;} int GetW() {return W;} private: //新增私有数据 int W,H; };
本类成员所需的形参 基类所需的形参 本类成员初始化赋值语句; Rectangle:: Rectangle(int x, int y, int w, int h): Point(x,y) { W=w; H=h; }; 基类构造函数 本类成员初始化赋值语句;
例9.5 定义一个派生类deriver,它是基类base1和base2的多继承。Deriver类还有两个私有的内嵌对象成员。定义派生类deriver的构造函数。
class base1 { private: int m_base_data; public: base1(int data){m_base_data=data;} //… };
class base2 { private: int m_base_data; public: base2(int data){m_base_data=data;} //… };
class Abc { private: float m_abc_data; public: Abc(float data){ m_abc_data=data; } //… };
class deriver:public base1, public base2 { private: Abc m_member1, m_member2; double m_deriver_data; public: deriver(int bd1, int bd2, float id1, float id2, double dd); };
deriver:: deriver(int bd1, int bd2, float id1, float id2, double dd): base1(bd1),base2(bd2), m_member1(id1), m_member2(id2) { m_deriver_data=dd; }
使用基类无参构造函数 deriver:: deriver(float id1, float id2, double dd): m_member1(id1), m_member2(id2) { m_deriver_data=dd; }
使用对象数据成员的无参构造函数 deriver:: deriver(int bd1, int bd2, double dd): base1(bd1),base2(bd2) { m_deriver_data=dd; }
派生类的构造函数 如果基类和对象数据成员的构造函数都无参数,派生类构造函数形参表中将只包含用于初始化它自己的基本类型数据成员的参数。 如果这个派生类恰好没有基本类型的数据成员,则其构造函数的形参表为空,可以不定义构造函数,而使用系统提供的默认构造函数。
使用系统提供的默认构造函数 #include<iostream> using namespace std; class base { private: int m_data; public: void SetData(int data){m_data=data;} int GetData(){return m_data;} };
class deriver:public base { private: int m_member; public: void SetMember(int m){ m_member=m;} int GetMember(){return m_member;} };
void main() { int n(10); deriver obj; obj.SetMember(n); cout<<obj.GetMember()<<endl; }
派生类的构造函数 基类的构造函数不被继承,需要在派生类中自行定义 。 定义构造函数时,以合适的初值为参数,初始化本类中新增成员。 利用成员初始化表隐含调用基类和新增对象数据成员的构造函数,初始化它们各自的数据成员。 构造函数的调用次序 系统会使用派生类构造函数的形参表的参数调用基类和内嵌对象成员的构造函数。
派生类的构造函数 系统在建立派生类对象时,首先调用基类的构造函数,再调用派生类的构造函数。 系统在建立组合类对象时,先调用内嵌子对象的构造函数,在调用组合类的构造函数。 如果一个派生类又是组合类,则系统先调用其基类的构造函数,再调用其内嵌子对象的构造函数,再后系统才调用派生类的构造函数。
派生类的构造函数 如果是多继承,系统调用基类构造函数的顺序是按照定义派生类时这些基类被继承的顺序进行的,与这些基类构造函数在派生类构造函数成员初始化列表的先后次序无关。 如果派生类有多个对象数据成员,则系统调用这些对象数据成员的构造函数的顺序是依据派生类定义这些成员的顺序进行的,与派生类成员初始化列表中对象数据成员构造函数排列先后次序无关。
deriver:: deriver(int bd1, int bd2, float id1, float id2, double dd): base2(bd2),base1(bd1), m_member2(id2) , m_member1(id1) { m_deriver_data=dd; } 构造函数的调用次序
9.3.2 派生类的析构函数 派生类不能继承基类的析构函数,需要自己定义析构函数,以便在派生类对象消亡之前进行必要的清理工作。 派生类的析构函数只负责清理它新定义的非对象数据成员,对象数据成员由对象成员所属类的析构函数负责析构。
#include<iostream> using namespace std; class base { private: int m_base_data; public: base(int data){m_base_data=data;} ~base(){cout<<"base object deconstruction"<<endl;} //… };
class Abc { private: float m_abc_data; public: Abc(float data){ m_abc_data=data; } ~Abc(){cout<<"Object member deconstruction"<<endl;} //… };
class deriver:public base { private: double m_deriver_data; Abc m_member1; int *m_ptr; public: deriver(int bd, float id, double dd); ~deriver(); void function(); };
deriver:: deriver(int bd, float id, double dd):base(bd), m_member1(id) { m_deriver_data=dd; m_ptr=new int[256]; if(m_ptr==NULL) cout<<"memory error in deriver obj"<<endl; }
deriver::~deriver() { if(m_ptr!=NULL) delete [] m_ptr; cout<<"Deriver obj deconstruction."<<endl; }
void deriver::function() { cout<<"Maybe you want to do something with m_ptr in this function. "<<endl; cout<<"Do as you like."<<endl; }
void main() { int n(1); float x(2.0f); double d(3.0); deriver obj(n,x,d); obj.function(); cout<<"The end of main function"<<endl; }
The end of main function Deriver obj deconstruction. 输出结果: Maybe you want to do something with m_ptr in this function. Do as you like. The end of main function Deriver obj deconstruction. Object member deconstruction base object deconstruction
派生类的析构函数 如果没有特殊指针数据成员需要清理,可以使用由系统提供的默认析构函数。 当派生类对象消亡时,系统调用析构函数的顺序与建立派生类对象时调用构造函数的顺序正好相反,即先调用派生类的析构函数,再调用其对象数据成员的析构函数,最后调用基类的析构函数。
9.4 多继承 9.4.1 多继承与二义性 多继承类结构中,派生类可能有多个直接基类或间接基类,充分体现了软件重用的优点,但也可能会引起成员访问的二义性或不确定性问题。 例9.7 多继承时的二义性
#include<iostream> using namespace std; class base {private: int m_data; public: base(int m) { m_data=m; cout<<"base construction"<<endl; } ~base(){cout<<"base deconstruction"<<endl;} void setdata(int data){m_data=data;} int getdata(int data){ return m_data;} };
class Fderiver1: public base { private: int m_value; public: Fderiver1(int value,int data):base(data) { m_value=value; cout<<"Fderiver1 construction"<<endl; } ~Fderiver1(){cout<<"Fderiver1 deconstruction"<<endl;} void setvalue(int value){ m_value=value;} int getvalue(){ return m_value; } void fun(){}; };
class Fderiver2: public base { private: int m_number; public: Fderiver2(int number,int data):base(data) { m_number=number; cout<<"Fderiver2 construction"<<endl; } ~Fderiver2(){cout<<"Fderiver2 deconstruction"<<endl;} void setnumber(int number){ m_number=number;} int getnumber(){ return m_number; } void fun(){}; };
class Sderiver: public Fderiver1, public Fderiver2 {private: int m_attrib; public: Sderiver(int attrib,int number,int value,int data): Fderiver1(value,data),Fderiver2(number,data) { m_attrib=attrib; cout<<"Sderiver construction"<<endl; } ~Sderiver(){cout<<"Sderiver deconstruction"<<endl;} void setattrib(int attrib){m_attrib=attrib;} int getattrib(){return m_attrib;} void newfun1(){}; int newfun2(){}; };
void main() { Sderiver object(3,4,5,6); object.setdata(7); } 产生二义性
9.4.2 虚基类 为解决二义性问题,将共同基类设置为虚基类,创建派生类对象时,虚基类的构造函数只会调用一次,虚基类的成员在第三层派生类对象中就只有一份拷贝,不会再引起二义性问题。
虚基类 语法形式: class 派生类名: virtual 继承方式 基类名 { //…… } 在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧随其后的基类起作用。
虚基类 在多继承类结构中,说明虚基类之后,虚基类的成员在派生类中将不会因继承关系对虚基类的多次继承而形成多份拷贝,只为最远的派生类提供唯一的基类成员,消除了多继承结构中的二义性问题。 需要注意的是在第一级继承时就要将共同基类设计为虚基类。
class Fderiver1: virtual public base { private: int m_value; public: Fderiver1(int data,int value):base(data) { m_value=value; cout<<"Fderiver1 construction"<<endl; } ~Fderiver1(){cout<<"Fderiver1 deconstruction"<<endl;} void setvalue(int value){ m_value=value;} int getvalue(){ return m_value; } void fun(){}; };
class Fderiver2: virtual public base { private: int m_number; public: Fderiver2(int data, int number):base(data) m_number=number; cout<<"Fderiver2 construction"<<endl; } ~Fderiver2(){cout<<"Fderiver2 deconstruction"<<endl;} void setnumber(int number){ m_number=number;} int getnumber(){ return m_number; } void fun(){}; };
class Sderiver: public Fderiver1, public Fderiver2 { private: int m_attrib; public: Sderiver(int data, int value, int number, int attrib): base(data),Fderiver1(data,value), Fderiver2(data,number) { m_attrib=attrib; cout<<"Sderiver construction"<<endl; } ~Sderiver(){cout<<"Sderiver deconstruction"<<endl;} void setattrib(int attrib){m_attrib=attrib;} int getattrib(){return m_attrib;} void newfun1(){}; int newfun2(){}; };
多继承派生类构造函数的定义 在包含虚基类的继承结构中,系统在建立派生类的对象时,调用构造函数的顺序是: (1)首先按照虚基类被继承的顺序,调用它们的构造函数; (2)其次按照非虚基类被继承的顺序,调用它们的构造函数; (3)再次按照对象数据成员声明的顺序,调用它们的构造函数; (4)最后调用派生类自己的构造函数。
多继承派生类构造函数的定义 如果基类使用带参的构造函数,则派生类需要在其构造函数的形式参数表中提供相应的参数给基类,对其对象数据成员亦如此。 如果基类和对象数据成员都使用默认构造函数,派生类也没有需要初始化的基本类型数据成员,也可以使用默认构造函数。 析构派生类的对象时,析构函数的调用顺序正好与构造函数的调用顺序相反。
#include<iostream> using namespace std; void main() { int d (1), v (2), n (3), a (4); Sderiver(d, v, n, a); //add some function,… cout<<"the end of main"<<endl; }
输出结果: base construction Fderiver1 construction Fderiver2 construction Sderiver construction Sderiver deconstruction Fderiver2 deconstruction Fderiver1 deconstruction base deconstruction the end of main
作业 第9章习题:6(1)(2),7