960 likes | 1.2k Views
第5章 继承和派生. 5.1 基类和派生类 5.2 单继承 5.3 多继承 5.4 虚基类. 5.1 基类和派生类. 5.1.1 派生类的定义格式 5.1.2 派生类的三种继承方式 5.1.3 访问控制 5.1.4 基类和派生类的关系. 返回首页. 称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。派生类可以具有基类的特性,共享基类的成员函数,使用基类的数据成员,还可以定义自己的新特性,定义自己的数据成员和成员函数。. 图5-1 类之间的继承与派生关系. 5.1.1 派生类的定义格式.
E N D
第5章 继承和派生 • 5.1 基类和派生类 • 5.2 单继承 • 5.3 多继承 • 5.4 虚基类
5.1 基类和派生类 • 5.1.1 派生类的定义格式 • 5.1.2 派生类的三种继承方式 • 5.1.3 访问控制 • 5.1.4 基类和派生类的关系 返回首页
称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。派生类可以具有基类的特性,共享基类的成员函数,使用基类的数据成员,还可以定义自己的新特性,定义自己的数据成员和成员函数。称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。派生类可以具有基类的特性,共享基类的成员函数,使用基类的数据成员,还可以定义自己的新特性,定义自己的数据成员和成员函数。
5.1.1 派生类的定义格式 单继承的定义格式如下: class<派生类名> :<继承方式><基类名> { public: //派生类新定义成员 members; <private:> members; <protected:> members; };
多继承的定义格式如下: class<派生类名> :<继承方式1><基类名1>,<继承方式2><基类名2>,… { public: //派生类新定义成员 members; <private:> members; <protected:> members; };
例5-1:写出下列程序的执行结果。 #include <iostream.h> class demo {protected: int j; public: demo(){j=0}; void add(int i){j+=i;} void display() { cout<<"Current value of j is"<<j<<endl;} }; class child:public demo //类child继承了demo类 { char ch; public: void sub(int i){j-=i;} };
void main(void) { child object,obj; //两个对象的j均为0 object.display(); object.add(10); object.display(); object.sub(5); object.display(); //object的j=5 obj.display(); //obj的j=0 } 此程序的运行结果为: current value of j is 0 current value of j is 10 current value of j is 5 current value of j is 0 返回本节
5.1.2 派生类的三种继承方式 • 在介绍公有继承(public)、私有继承(private)和保护继承(protected)的继承方式前,先看一个例子。 • 例5-2:汽车类vehicle和其派生类小车类car的定义。 class vehicle { int wheels ; //车轮数 float weight ; //汽车重量 public :
void initialize (int in_wheels , float in_weight ); //初始化数据成员 int get_wheels ( ) ; //获取车轮数 float get_weight ( ) ; //获取车重 }; class car : vehicle //默认声明,私有继承 { int passenger_load : //载客量 public : void initialize (int in_wheels,float in_weight,int people=4); int passengers ( ) ; //返回载客数 };
常用的三种继承方式: • 1.公有继承(public) 公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。 • 2.私有继承(private) 私有继承的特点是基类的公有成员和保护成员作为派生类的私有成员,并且不能被这个派生类的子类访问。 • 3.保护继承(protected) 保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。
例5-3:调用基类的公有成员示例程序。 #include<iostream.h> #include<math.h> 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: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; }; void main() { rectangle rect; rect.initr(2,3,20,10); rect.move(3,2); cout<<"the data of rect(x,y,w,h):"<<endl; cout<<rect.getx()<<","<<rect.gety()<<","<<rect.getW()<<","<< rect.getH()<<endl; }
例5-4:私有继承示例程序。 #include<iostream.h> #include<math.h> 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; }; void main() {
rectangle rect; rect.initr(2,3,20,10); rect.move(3,2); cout<<"the data of rect(x,y,w,h):"<<endl; cout<<rect.getx()<<","<<rect.gety()<<","<<rect.getW()<<","<< rect.getH()<<endl; } 返回本节
5.1.3 访问控制 • 类通过派生定义,形成类的等级,派生类中用“类名 :: 成员”访问基类成员。在建立一个类等级后,通常创建某个派生类的对象来使用这个类等级,包括隐含使用基类的数据和函数。 • 派生类对基类成员可以有不同的访问方式: • 派生类可以覆盖基类成员 • 派生类不能访问基类的私有成员 • 基类的公有段和保护段成员访问权对派生类保持不变(公有继承) • 基类的公有段和保护段成员成为派生类的私有成员(私有继承)
例5-6:给出以下程序的运行结果。 #include < iostream.h > class Base { public : Base ( ) { cout << "\nBase created.\n" ; } } ; class D_class : public Base { public : D_class ( ) { cout << "D_class created.\n" ; } } ; main ( ) { D_class d1 ; } 此程序的运行结果为: Base created. D_class created.
1.定义与派生类同名的成员 例如: class base { public : int a , b ; }; class derived : public base { public : int b , c ; } ; void f ( ) { derived d ; d . a = 1 ; d . base :: b = 2 ; // base :: b 使用的是base 类的数据成员b d . b = 3 ; //这里使用的是derived 类的数据成员b d . c = 4 ; };
2.派生类不能访问基类私有成员 例如: class X {public : void get_ij ( ) ; void put_ij ( ) ; private : int i , j ; }; class Y : public X {public : int get_k ( ) ; void make_k ( ) ; private : int k ; }; void Y :: make_k ( ) ; { k = i * j ; // 非法 } ;
3.公有继承 例5-7:派生类对基类的公有继承示例。 class X { protected : int i , j ; public : void get_ij( ) { cout<<“Enter two numbers:”; cin>>i>>j;}; void put_ij( ) { cout<<i<<" "<<j<<'\n';}; }; class Y : public X { int k ; public : int get_k( ) { return k ; }; void make_k( )
{ k = i * j ; }; // 使用基类成员i,j }; class Z : public Y { public : void f( ) { i = 2 ; j = 3; }; // 使用基类成员i,j }; main ( ) { Y var1; Z var2 ; var1.get_ij( ) ; var1.put_ij( ) ; var1.make_k( ) ; var2.f( ) ; var2.put_ij( ) ; }
4.私有继承和保护继承 • 私有继承:派生类对基类的私有继承使用关键字private描述(可缺省),基类的所有公有段和保护段成员都成为派生类的私有成员。 • 保护继承:派生类对基类的保护继承使用关键字protected描述,基类的所有公有段和保护段成员都成为派生类的保护成员,保护继承在程序中很少应用。
例5-8:派生类对基类的私有继承示例。 class X {protected : int i , j ; public : void get_ij ( ) {cout<<“Enter two numbers:”; cin>>i>>j;}; void put_ij ( ) {cout<<i<<“”<<j<<‘\n’;}; }; class Y : private X { int k ; public : int get_k ( ) { return k ; }; void make_k ( ) { k = i * j ; } ; } ; class Z : public Y { public : void f( )
{ i = 2 ; j = 3 ; // error }; }; main ( ) { Y var1; Z var2 ; var1 . get_ij ( ) ; // error var1 . put_ij ( ) ; // error var1 . make_k ( ) ; cout << var1 . get_k ( ) << ‘\n’ ; var2 . put_ij ( ) ; // error }
5.派生类中的静态成员 例如: class B { public : static void f ( ) ; void g ( ) ; } ; class D : private B { } ; class DD : public D { void h ( ) ; } ; void DD :: h ( ) { B :: f ( ) ; // ok f ( ) ; // error g ( ) ; // error } ;
6.访问声明 例如: class B { int a ; public : int d , c ; int bf ( ) ; } ; class D : private B { int d ; public : B :: c ; // 调整对B :: c的访问控制 int e ; int df ( ) ; } ; 返回本节
5.1.4 基类和派生类的关系 • 1.派生类是基类的具体化 类的层次通常反映了客观世界中某种真实的模型。 • 2.派生类是基类定义的延续 先定义一个抽象基类,该基类中有些操作并未实现。 • 3.派生类是基类的组合 在多继承时,一个派生类有多于一个的基类,这时派生类将是所有基类行为的组合。 返回本节
5.2 单继承 • 5.2.1 成员访问权控制 • 5.2.2 派生与构造函数、析构函数 • 5.2.3 继承中构造函数的调用顺序 • 5.2.4 组合 • 5.2.5 子类型和类型适应 返回首页
5.2.1 成员访问权控制 • 例5-9:public继承方式访问权限的例子。 #include <iostream.h> //定义基类A class A { public: A() { cout<<"类A的构造函数!"<<endl; } A(int a) { Aa = a, aa = a, aaa = a; } void Aprint() { cout<<"类A打印自己的private成员aa:"<<aa<<endl; } int Aa; private:
int aa; protected: int aaa; }; //定义由基类A派生的类B class B : public A { public: B() { cout<<"类B的构造函数!"<<endl; } B(int i, int j, int k); void Bprint() { cout<<"类B打印自己的private成员bb和protected成员bbb:" <<bb<<","<<bbb<<endl; } void B_Aprint()
{ cout<<"类B的public函数访问类A的public数据成员Aa:"<<Aa<<endl; cout<<"类B的public函数访问类A的protected数据成员aaa:"<<aaa<<endl; GetAaaa(); GetAaaa1();} private: int bb; void GetAaaa() { cout<<"类B的private函数访问类A的public数据成员Aa:"<<Aa<<endl; cout<<"类B的private函数访问类A的protected数据成员aaa:"<<aaa<<endl;} protected: int bbb;
void GetAaaa1() { cout<<"类B的protected函数访问类A的public数据成员Aa:"<<Aa<<endl; cout<<"类B的protected函数访问类A的protected数据成员aaa:"<<aaa<<endl;} }; //类B的构造函数,需负责对基类A的构造函数的初始化 B::B(int i, int j, int k):A(i), bb(j), bbb(k) {} //程序主函数 void main() {
B b1(100, 200, 300); //定义类B的一个对象b1,并初始化构造函数和基类构造函数 b1.B_Aprint(); //类B调用自己的成员函数B_Aprint函数 b1.Bprint(); //类B对象b1访问自己的private和protected成员 b1.Aprint(); //通过类B的对象b1调用类A的public成员函数 }
该程序的输出结果为: 类B的public函数访问类A的public数据成员Aa:100 类B的public函数访问类A的protected数据成员aaa:100 类B的private函数访问类A的public数据成员Aa:100 类B的private函数访问类A的protected数据成员aaa:100 类B的protected函数访问类A的public数据成员Aa:100 类B的protected函数访问类A的protected数据成员aaa:100 类B打印自己的private成员bb和protected成员bbb:200,300 类A打印自己的private成员aa:100 返回本节
5.2.2 派生与构造函数、析构函数 • 1.构造函数 派生类的对象的数据结构由基类中说明的数据成员和派生类中说明的数据成员共同构成。将派生类的对象中由基类中说明的数据成员和操作所构成的封装体称为基类子对象,它由基类中的构造函数进行初始化。构造函数不能够被继承。
派生类构造函数的一般格式如下 <派生类名>(<派生类构造函数总参数表>):<基类构造函数>(<参数表1>), <子对象名>(<参数表2>) { <派生类中数据成员初始化> }; 派生类构造函数的调用顺序如下: (1)调用基类的构造函数,调用顺序按照它们继承时说明的顺序。 (2)调用子对象类的构造函数,调用顺序按照它们在类中说明的顺序。 (3)派生类构造函数体中的内容。
例5-10:构造派生类构造函数的例子。 #include <iostream.h> class A { public: A() { a=0; cout<<"类A的缺省构造函数.\n"; } A(int i) { a=i; cout<<"类A的构造函数.\n"; } ~A() { cout<<"类A的析构函数.\n"; } void Print() const { cout<<a<<","; } int Geta() { return a; } private: int a; };
class B : public A { public: B() { b=0; cout<<"类B的缺省构造函数.\n"; } B(int i, int j, int k); ~B() { cout<<"类B的析构函数.\n"; } void Print(); private: int b; A aa; }; B::B(int i, int j, int k):A(i), aa(j) { b=k; cout<<"类B的构造函数.\n"; } void B::Print() {
A::Print(); cout<<b<<","<<aa.Geta()<<endl; } void main() { B bb[2]; bb[0] = B(1, 2, 3); bb[1] = B(4, 5, 6); for(int i=0; i<2; i++) bb[i].Print(); }
运行结果为: 类A的缺省构造函数. 类A的缺省构造函数. 类B的缺省构造函数. 类A的缺省构造函数. 类A的缺省构造函数. 类B的缺省构造函数. 类A的构造函数. 类A的构造函数. 类B的构造函数. 类B的析构函数. 类A的析构函数. 类A的析构函数.
类A的构造函数. 类A的构造函数. 类B的构造函数. 类B的析构函数. 类A的析构函数. 类A的析构函数. 1,3,2 4,6,5 类B的析构函数. 类A的析构函数. 类A的析构函数. 类B的析构函数. 类A的析构函数. 类A的析构函数.
2.析构函数 • 例5-11:析构函数执行顺序示例程序。 #include<iostream.h> class B1 { public: B1(int i){cout<<"constructing B1"<<i<<endl;} ~B1(){cout<<"destructing B1"<<endl;}; }; class B2 { public: B2(int j){cout<<"constructing B2"<<j<<endl;} ~B2(){cout<<"destructing B2"<<endl;} }; class B3 {
public: B3(){cout<<"constructing B3"<<endl;} ~B3(){cout<<"destructing B3"<<endl;} }; class C:public B2,public B1,public B3 { public: C(int a,int b,int c,int d):B1(a),memberB2(d),memberB1(c),B2(b){} private: B1 memberB1; B2 memberB2; B3 memberB3; }; void main() { C obj(1,2,3,4); }
运行结果为: constructing B22 constructing B11 constructing B3 constructing B13 constructing B24 constructing B3 destructing B3 destructing B2 destructing B1 destructing B3 destructing B1 destructing B2
3.派生类构造函数使用中应注意的问题 (1)派生类构造函数的定义中可以省略对基类构造函数的调用,其条件是在基类中必须有默认的构造函数或者根本没有定义构造函数。 (2)当基类的构造函数使用一个或多个参数时,则派生类必须定义构造函数,提供将参数传递给基类构造函数的途径。
例5-12:请写出下列程序的运行结果。 #include<iostream.h> class Vehicle //汽车类 {protected: int numWheels; //车轮数 int range; //汽车活动范围 public: Vehicle(int w,int r):numWheels(w),range(r){} void ShowV() { cout<<”Wheels:”<<numWheels<<endl; cout<<”Range:”<<range<<endl; } };
class Car : public Vehicle //小轿车 { int passengers; //乘客数 public: Car(int w,int r, int p):Vehicle(w,r),passengers(p){} void Show() { cout<<"\nCar:\n"; ShowV(); cout<<"Passengers: "<<passengers<<endl; } } void main() { Car c(4,500,5); c.Show(); }