Presentation is loading. Please wait.

Presentation is loading. Please wait.

第四章 继承和派生类 汽车 专用汽车 运输汽车 货车 客车 消防车 洒水车 最普遍、最一般,可以自行驱动 含有汽车的特性,同时与汽车有不同

Similar presentations


Presentation on theme: "第四章 继承和派生类 汽车 专用汽车 运输汽车 货车 客车 消防车 洒水车 最普遍、最一般,可以自行驱动 含有汽车的特性,同时与汽车有不同"— Presentation transcript:

1 第四章 继承和派生类 汽车 专用汽车 运输汽车 货车 客车 消防车 洒水车 最普遍、最一般,可以自行驱动 含有汽车的特性,同时与汽车有不同
第四章 继承和派生类 汽车 最普遍、最一般,可以自行驱动 专用汽车 运输汽车 含有汽车的特性,同时与汽车有不同 货车 客车 消防车 洒水车 /

2 继承和派生的基本概念 要声明雇员类employee 例如:现有person类 class Employee{ class Person{
private: char name[10]; int age; char sex; char department[20]; float salary; public: void print(); }; class Person{ private: char name[10]; int age; char sex; public: void print(); }; 继承可以避免代码的严重重复,提高代码的可重用性

3 继承和派生的基本概念 类的派生:通过特殊化已有的类来建立新类的过程 基类(父类):原有的类:例如Person类
派生类(子类):新建立的类:例如Employee类 继承:派生类自动地将基类的所有成员作为自己的成员 派生新类时,可以对派生类做的变化: 可增加新的成员变量 可增加新的成员函数 可重新定义已有的成员函数 可改变现有成员的属性

4 单一继承 声明一个派生类的一般格式为: class 派生类名:访问控制 基类名{ //派生类新增的数据成员和成员函数 };
访问控制:用于规定基类成员在派生类中的访问权限。缺省时为private

5 单一继承 1.定义基类(person类) 2.定义派生类(employee类) class person{ private:
char name[10]; int age; char sex; public: void print(); }; class employee:public person{ char department[10]; float salary; public: }; 派生类employee继承类person的全部特性。

6 单一继承 class Location{ private: int X,Y; public:
void SetXY(int mx,int my){X=mx;Y=my;} void showxy() {cout<<"X="<<X<<" "<<"Y="<<Y<<endl;} }; class Rectangle:public Location{ int H,W; void SetHW(int mh,int mw){H=mh;W=mw;} void show() {cout<<"H="<<H<<" "<<"W="<<W<<endl;} void main() { Rectangle r1; r1.SetXY(3,5); r1.SetHW(4,6); r1.showxy(); r1.show(); }

7 类的保护成员 基类中的私有成员不允许派生类中的成员函数访问,但可通过基类提供的公有成员函数访问 来自基类 r1.showxy();
来自派生类 void Rectangle::show() {cout<<"X="<<X<<" "<<"Y="<<Y<<"H="<<H<<" "<<"W="<<W<<endl;} 出错:问题在哪里? X和Y是从Location继承来的;H和W是Rectangle自己定义的 类的私有成员是只能被它的成员函数和友元访问的 基类中的私有成员不允许派生类中的成员函数访问,但可通过基类提供的公有成员函数访问

8 类的保护成员 保护成员:protected成员。对派生类的成员函数而言,它是公有成员,可以被访问;对其他函数而言,它是私有成员,不能被访问
class Location{ protected: int X,Y; public: void SetXY(int mx,int my){X=mx;Y=my;} void showxy() {cout<<"X="<<X<<" "<<"Y="<<Y<<endl;}}; class Rectangle:public Location{ private: int H,W; void SetHW(int mh,int mw){H=mh;W=mw;} void show() {cout<< "X="<<X<<" "<<"Y="<<Y "H="<<H<<" "<<"W="<<W<<endl;} }; void main() { Rectangle r1; r1.X=3; r1.Y=5; r1.SetHW(4,6); r1.show(); } r1.SetXY(3,5)

9 访问权限和赋值兼容规则 派生方式: 公有派生:关键字public 私有派生(默认值):关键字private

10 公有派生和赋值兼容规则 基类成员的访问权限 基类的公有成员在派生类中仍然是公有的 基类的保护成员在派生类中仍然是保护的
基类的不可访问的和私有的成员在派生类中也仍然是不可访问的

11 公有派生和赋值兼容规则 赋值兼容规则(前提是公有派生) 派生的对象可以赋给基类的对象 Base b; Derived d; b=d;
派生类的对象可以初始化基类的引用 Derived d; base &br=d; 派生类的对象的地址可赋给指向基类的指针 Derived d; base &bptr=&d;

12 公有派生和赋值兼容规则 指向基类对象成员的指针也可以赋给指向派生类对象成员的指针 Derived *dptr;
Base *bptr=dptr; 通过赋值,对象b,br及指针bptr,已经具有对象d中相应数据成员的值,或者和指针dptr指向同一个对象。但是,通过b,br或bptr只能使用对象d(或dptr指向的对象)中从基类Base继承的成员,而不能使用派生类Derived中新增的成员。

13 私有派生 私有派生:基类中的所有成员在派生类中都是私有的 基类 派生类,私有继承base类的所有特性
class base{ int x; public: void setx(int n) {x=n;} void showx() {cout<<x<<'\n';} }; class derived:private base{ int y; public: void setxy(int n,int m) { setx(n); y=m; } void showxy() {cout<<x<<y<<'\n';} 错误,引用基类的私有成员 派生类,私有继承base类的所有特性 {showx();cout<<y<<'\n';} main() { derived obj; obj.setxy(10,20); obj.showxy(); return 0; }; 结论:基类中的私有成员既不能被外部函数访问,也不能被派生类成员函数访问,只能被基类的成员函数访问。

14 私有派生 错误,setx是私有成员 结论:私有继承时,基类的私有、公有成员成为派生类的私有成员,不能被外界访问。 class base{
int x; public: void setx(int n) {x=n;} void showx() {cout<<x<<'\n';} }; class derived:private base{ int y; void sety(int n) { setx(10); y=n;} void showy() {showx();cout<<y<<'\n';} void main() { derived obj; base obj1; obj.setx(10); obj.sety(20); obj1.setx(10); }; 结论:私有继承时,基类的私有、公有成员成为派生类的私有成员,不能被外界访问。 错误,setx是私有成员

15 保护成员 class samp{ int a; protected: int b; public: int c;
samp(int n,int m) {a=n;b=m;} int geta() {return a;} int getb() {return b;} }; void main() { samp obj(20,30); obj.b=99; obj.c=50; cout<<obj.geta()<<' '; cout<<obj.getb()<<' '<<obj.c<<'\n'; } 错误,外部函数不可访问保护成员

16 保护成员 保护成员以公有方式被继承后---成为保护成员 class base{ protected: int a,b; public:
void setab(int n,int m) {a=n;b=m;} }; class derive:public base{ int c; void setc(int n) {c=n;} void showabc() {cout<<a<<' '<<b<<' '<<c<<'\n';}//允许访问保护成员

17 保护成员 保护成员以私有方式被继承后—成为私有成员 注意:protected部分声明的所有标识符可以在类实现中使用,但是不能被类的用户使用
class base{ protected: int a; public: void seta(int sa) {a=sa;} }; class derive1:private base{ int b; void setb(int sb) {a=sb;} class derive2:public derive1{ int c; void setc(int sc) {c=sc;} void show() { cout<<"a="<<a<<'\n'; cout<<"b="<<b<<'\n'; cout<<"c="<<c<<'\n'; }}; 注意:protected部分声明的所有标识符可以在类实现中使用,但是不能被类的用户使用

18 总结继承的访问权限 公有派生:基类中成员的访问权限在派生类中保持不变 私有派生:基类中成员的访问权限在派生类中都是私有的
影响派生类的对象对基类中定义的成员的可访问性 影响从该派生类进一步派生出的派生类的成员函数对这个基类成员的可访问性。

19 保护方式的派生 公有基类成员在派生类中是保护的 派生类定义可访问这些保护成员,但派生类对象不能访问这些保护成员

20 多重继承 定义:一个派生类具有多个基类的派生方法 多重继承声明的形式 class 派生类名:访问控制 类名1,…,访问控制n 类名n{
//派生类新增的数据成员和成员函数 };

21 class A{ private: int a; public: void setA(int x){a=x;} void showA(){cout<<“a=“<<a<<endl;} }; class B{ int b; void setB(int x){b=x;} void showB(){cout<<“b=“<<b<<endl;} class C:public A,private B{ int c; void setC(int x,int y){c=x;setB(y);} void showC(){showB();cout<,c<<endl;} void main() {c obj; obj.setA(53); obj.showA(); obj.setC(55,58); obj.showC(); obj.setB(5) obj.showB();}

22 冒号后的列表称为成员初始化列表,表中各项用逗号隔开,参数表给出所调用的构造函数所需要的实参。
构造函数与析构函数调用顺序 定义派生类的构造函数的一般形式为: Y::Y(参数表0):X1(参数表1),X2(参数表2),…Xn(参数表n) {//… } 对于单一继承允许省略基类名 Y::Y(参数表0):(参数表1) 冒号后的列表称为成员初始化列表,表中各项用逗号隔开,参数表给出所调用的构造函数所需要的实参。 省略初始化列表的条件:基类必须定义有缺省(无参)构造函数或根本没有构造函数。

23 构造函数与析构函数调用顺序 派生类构造函数和析构函数的执行顺序 派生类构造函数和析构函数的构造规则
先执行基类的构造函数,随后执行派生类的构造函数 先执行派生类的析构函数,随后执行基类的析构函数 派生类构造函数和析构函数的构造规则 派生类不能继承基类中的构造函数和析构函数 基类含有带参数的构造函数时,派生类必须定义构造函数

24 class base{ int i; public: base(int n) 带参数的构造函数 {cout<<“Constructing base class\n”; i=n;} ~base() {cout<<"Destructing base class\n";} void showi() {cout<<i<<'\n';} }; class derive:public base{ 公有继承类base int j; derive(int n,int m):base(m) 派生类的构造函数 {cout<<"Constructing derived class\n"; j=n;} ~derive() {cout<<"Destructing derived class\n";} void showj() {cout<<j<<'\n';} void main() { derive obj(30,40); 定义类derive的对象时,调用构造函数:数据成员i=40,j=30 obj.showi(); obj.showj(); }

25 构造函数与析构函数调用顺序 派生类含有对象成员时,构造函数的一般形式为 构造函数的执行顺序(撤消和其相反) 基类的构造函数
Y(参数表):X1(参数表),对象成员名1(参数表),…,对象成员名n(参数表) 构造函数的执行顺序(撤消和其相反) 基类的构造函数 对象成员的构造函数 派生类的构造函数

26 class base{ private: int a; public: base(int i) {a=i;cout<<"constructing base a="<<a<<endl;} ~base(){cout<<"destroying base a="<<a<<endl;} }; class derived:public base{ int d; base member; derived(int i,int j,int k):base(i),member(j) {d=k;cout<<"constructing derived d="<<d<<endl;} ~derived() {cout<<"desrtoying derived d="<<d<<endl;} void main() {derived d1(5,8,9);}

27 构造函数与析构函数调用顺序 说明: 基类构造函数不带参数时,派生类不一定需要定义构造函数,但当基类的构造函数只要带一个参数,它所有的派生类都必须定义构造函数 基类使用缺省构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去“:基类构造函数名(参数表)” 基类和派生类的析构函数是独立的。 如派生类的基类也是一个派生类,则每个派生类只需负责其直接基类的构造

28 构造函数与析构函数调用顺序 class A {A( )}; class B:public A{ B( ):A( ){} };
class C:public B{ C( ):B( ){} 不能写成c( ):A( ),B( ){},只能调用自己的直接基类。实际执行时,先调用A类构造函数,再调用B类构造函数,最后调用C类构造函数。

29 多重继承时构造函数与析构函数调用顺序 构造函数定义的形式:X(参数表):X1(参数表),X2(参数表),…
基类构造函数执行顺序按它们被继承时所说明的顺序(从左到右)依次调用。

30 构造函数调用顺序: base2 base1 base3 base4 class base1{ public:
base1(){cout<<“constructing base1”<<endl;} ~base1(){cout<<“destorying base1”<<endl;} }; class base2{ base2(){cout<<“constructing base2”<<endl;} ~base2(){cout<<“destroying base2”<<endl;} class base3{ base3(){cout<<“constructing base3”<<endl;} ~base3(){cout<<“destorying base3”<<endl;} class base4{ base4(){cout<<“constructing base4”<<endl;} ~base4(){cout<<“destorying base4”<<endl;} class derived:public base2,public base1,public base3{ privated: base4 memobj;}; 构造函数调用顺序: base2 base1 base3 base4

31 总结构造函数与析构函数调用顺序 结论:先执行基类的构造函数,再执行对象成员的构造函数,最后执行派生类构造函数。多个基类之间,按照派生类声明时从左到右的顺序来排列先后。

32 两义性 两义性和作用域分辨操作符 class A{ public: void func( ) … }; class B{
class C:public A,public B{ void hunc( ){func( );} 哪个类的func函数? 类c的对象obj obj.func()访问的 是哪个类的函数func 使用成员名限定可以消除两义性 A::func( ); obj.A::func();

33 两义性 class B{ public: void gunc( )… }; class C:public B{
类C有两个函数gunc,一个继承类B,一个自己定义 该状况无两义性: 基类中的函数名字在派生类中再次声明,则派生类中的函数名字隐藏基类中的相应名字 如果引用B类的gunc函数,则使用作用域操作符 类名::类标识符 如:B::gunc( )

34 支配规则 支配规则:类X中的名字N支配类Y(基类)中同名的名字N。 说明:
支配规则指派生类中重定义基类的函数,只要函数名相同,函数参数与返回值皆可不同于基类,编译器看到同名函数名,便将基类的函数隐藏,至于参数表及返回值是否相同则不考虑。

35 两义性 派生类从多个基类派生,这些基类又有一个共同的基类,在派生类中访问这个共同的基类中的成员产生两义性 base2 base base1
derived class base{ public:int b;}; class base1:public base{}; class base2:public base{}; class derived:public base1,public base2{ public:int fun( );}; derived d; d.b; 错,不知从哪一条路径上继承 d.base::b; 错,不知从哪一条路径上继承

36 两义性 结论 derived类的对象包含数据成员b的两个实例 建立derived类的对象时base的构造函数调用两次
一个类不能从同一个类中直接继承一次以上 class derived:public base,public base{ …}

37 两义性和访问权限 两义性检查在前,访问权限不能阻止两义性检查 class A{ public:void fun( ); };
class B{ private:void fun( ); class C:public A,public B{}; C.obj; obj.fun(); 两义性检查在前,访问权限不能阻止两义性检查

38 void main() { Derived *ptr; ptr->x=1; ptr->fun1(); ptr->fun2(); ptr->fun2(10); ptr->fun3();} class Base1{ public: int x; int fun1(); int fun2(); int fun2(int); int fun3(); }; class Base2{ char fun2(); private: class Derived:public Base1,public Base2{ };

39 class One{ public: int a; void b(); void c(float);}; class Two{ void c(); private: int a;}; class Three:public One,public Two{ void b();}; void main() { Three obj; Obj.a=1; Obj.b(); Obj.c(10);}

40 格式:派生类:virtual 访问控制 基类
虚基类 虚基类:使公共的基类产生一个实例 格式:派生类:virtual 访问控制 基类 说明:关键字virtual和关键字public或private的相对位置无关紧要,但要放在基类名之前,并且关键字virtual只对紧随其后的基类名起作用。 d.b 因为一个实例,正确 base2 base base1 derived d.base1::b 和 d.base2::b具有相同的值

41 例题分析 C) C++语言建立类族是通过 实现的 A)类的嵌套 B)虚函数 C)类的继承 D)抽象类
继承具有 即当基类本身也是某一个类的派生类时,底层的派生类也会自动继承间接基类的成员 A)规律性 B)传递性 C)重复性 D)多样性 A) B) 提供了类对外部的接口, 是类的内部实现,而 不允许外界访问,但允许派生类的成员访问,这样既有一定的隐藏能力,又提供了开放的接口。 A)公有成员 B)私有成员 C)私有成员函数 D)保护成员 D)

42 例题分析 D) 下列对派生类的描述中,错误的是 A) 一个派生类可以作为另一个派生类的基类 B) 派生类至少有一个基类
C)  派生类的缺省继承方式是private D)  派生类只继承了基类的公有成员和保护成员 下列关于继承的描述中,错误的是 A)析构函数不能被继承 B)派生类是基类的组合 C)派生类的成员除它自己的成员外,还包含了它的基类的成员 D)派生类中继承的基类成员的访问权限到派生类保持不变 D) A) 派生类的对象对它的基类成员中 是可以访问的 A)  公有继承的公有成员 B)  公有继承的私有成员 C)  公有继承的保护成员 D)  私有继承的公有成员

43 例题分析 下列虚基类的声明中,正确的是 A) class virtual B:public A
B)  virtual class B:public A C)  class B:public A virtual D)  class B:virtual public A D) 下面 的叙述不符合赋值兼容规则 A)  派生类的对象可以赋值给基类的对象 B)  基类的对象可以赋值给派生类的对象 C)  派生类的对象可以初始化基类的引用 D)  派生类的对象的地址可以赋值给指向基类的指针 B)

44 例题分析 下列程序中,划线处正确的语句是 class Base { public:
void fun(){cout<< “Base::fun”<<endl;} }; class Derived:public Base void fun(){ 显式调用基类的函数fun() cout<< “Derived::fun”<<endl; } A)fun(); B)Base.fun(); C)Base::fun(); D)Base->fun(); C)

45 例题分析 A) C) 下列程序中,编译不会出错的语句是 class Base{ int data; public: Base( ){}
Base(int i){data=i;}}; class Derived:public Base{ Derived( ):Base(0){ } Derived(int x):Base(x){ }}; void main() { Derived d1(1); Base *pb,b1; pb=&d1; //A d1=b1; //B Base &refB=d1; Derived d2=&pb; //C Derived &refD=refB; //D } C)

46 class Person{ public: Person(){cout<< “Constructor of Person”endl;} ~Person(){cout<< “Destructor of Person”<<endl;} }; class Student:public Person{ Student(){cout<< “Constructor of Student”endl;} ~Student(){cout<< “Destructor of Student”<<endl;} class Teacher:public Person{ Teacher(){cout<< “Constructor of Teacher”endl;} ~Teacher(){cout<< “Destructor of Teacher”<<endl;} void main() { Student s; Teacher t; }

47 class Data{ int x; public: Data(int x) { Data::x=x; cout<< “Data cons.”<<endl; } ~Data(){cout<< “Data des.”<<endl;}}; class Base{ Data d1; Base(int x):d1(x){cout<< “Base cons.”<<endl;} ~Base(){cout<< “Base des.”<<endl;}}; class Derived:public Base{ Data d2; Derived(int x):Base(x),d2(x){cout<< “Derived cons.”<<endl;} ~Derived(){cout<< “Derived des.”<<endl;}}; void main() { Derived obj(5);}


Download ppt "第四章 继承和派生类 汽车 专用汽车 运输汽车 货车 客车 消防车 洒水车 最普遍、最一般,可以自行驱动 含有汽车的特性,同时与汽车有不同"

Similar presentations


Ads by Google