第章继承与派生newppt课件_图文

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
第4篇面向对象的程序设计
第11章 继承与派生 第12章 多态性与虚函数 第13章 输入输出流 第14章 C++工具
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
第11章 继承与派生
11.1 继承与派生的概念 11.2 派生类的声明方式 11.3 派生类的构成 11.4 派生类成员的访问属性 11.5 派生类的构造函数和析构函数 11.6 多重继承 11.7 基类与派生类的转换 11.8 继承与组合 11.9 继承在软件开发中的重要意义
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
面向对象程序设计有4个主要特点: 抽象 封装 继承 多态性
面向对象技术强调软件的可重用性(software reusability) 。C++语言提供了类的继承机制,解 决了软件重用问题。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.1 继承与派生的概念
?在C++中可重用性是通过继承(inheritance)这一机 制来实现的。继承是C++的一个重要组成部分。 ?在C++中,所谓“继承”就是在一个已存在的类 的基础上建立一个新的类。 ?见图11.1示意。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
已存在的类(例如“马”)称为“基类(base class)” 或“父类(father class)”。新建立的类(例如“公 马”)称为“派生类(derived class)”或“子类(son class)”。见图11.2示意。

图11.1
《C++程序设计 》网 络 教 学

图11.2

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
一个派生类只从一个基类派生,这称为单继承 (single inheritance),这种继承关系所形成的层次 是一个树形结构,见下图。
箭头表示继承的方向,从派生类指向基类。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
一个派生类不仅可以从一个基类派生,也可以从 多个基类派生。一个派生类有两个或多个基类的称 为多重继承(multiple inheritance)。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
关于基类和派生类的关系:派生类是基类的具体化, 而基类则是派生类的抽象。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.2 派生类的声明方式
声明派生类的一般形式为: class 派生类名: [继承方式] 基类名 { 派生类新增加的成员 };
?继承方式包括: public(公用的),private(私有的) 和protected(受保护的),如果不写此项,则默认为 private(私有的)。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.3 派生类的构成 派生类分为两大部分:
一部分是从基类继承来的成员 另一部分是在声明派生类时增加的部分 每一部分均分别包括数据成员和成员函数。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
构造一个派生类包括以下4部分工作:

编制 派生 类时 可分 四步

吸收基类的成员

不论是数据成员,还是函数成员, 除构造函数与析构函数外全盘接收

改造基类成员

声明一个和某基类成员同名的新成员,派 生类中的新成员就屏蔽了基类同名成员称 为同名覆盖(override)

派生类新成员必须与基类成员不同名,它的
发展新成员 加入保证派生类在功能上有所发展。

重写构造函数与析构函数
《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

派生类对象结构

基类对象

对于下面的继承关系:

a b

子类对象
a b c

基类部分 子类添加部分

子类对象空间总是不小于基类 对象

class Father{ int a,b; public: // 成员函数 }; class Son:public Father{ int c; public: // 成员函数 };

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.4 派生类成员的访问属性
继承可以公有继承,保护继承和私有继承。 公有继承是普通继承,基类可以为大多数应用服 务。也可以重复继承。 保护继承是“单传”继承,只继承给自己的后代, 应用是以子孙的公有成员函数来对外展开服务的。 私有继承是“绝版”继承,该基类只继承直接的 子类,而不考虑让子类再继承下去。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.4.1 公有继承
public继承: ?基类的public成员, 在派生类中成为public成员 ?基类的protected成员, 在派生类中成为protected 成员 ?基类的private成员, 在派生类中成为不可直接使用的成员

表11.1公用基类在派生类中的访问属性

公有基类的成员

私有成员 公有成员 保护成员

在公有派生类中的访问属性 不可访问 公有

保护

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

例:下面定义了两个具 有单继承关系的类A和 B,其中, A为基类,B为派生类:

class A {
int x,y; public:
void f(); void g(); }; class B : public A { int z; public: void h(); };

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

在C++中对派生类有以下几点规定:
class A
?派生类{ 除了拥有基类的所有成员(基类的构造函数

和赋值操作符in重t x载,y;函数除外)外,也可以具有新的成

员。

public: void f();

void g(); }; class B : public A {
int z; public:

void main()
{ B b; b.f( ); //A类中的 f b.g( ); //A类中的g b.h( ); //B类中的h

void h();

}

};

《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

?派生类的c{la定ss义Bi用n:t于pzu;描bli述c A派生类与基类的差别。派生 类中可以给pu出bl新ic:的成员,也可以对基类的成员进行重

定义。如果在派vv生ooii类dd hf中(());对基类的某个成员进行重定义, 则在派生类中对{该f(成);员//B的类访中问的是f 针对派生类中重定义

的成员。

g( ); //A类中的g }

};

void main( )

{ B b;

b.f( ); //B类中的 f

b.g ( ); //A类中的g

b.h( ); //B类中的h

}

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

?派生类成员cla名ss的B作: p用ub域li嵌c A套在基类的作用域中,对

于基类的一个{pu成bl员ici:n,t 如z; 果派生类中没有定义与其同名

的成员,则该成员v名oid在f派( );生类的作用域内可见,否则,

该用成 ,员必名 须在 用派 基生 类类 名v{的 受Aof(i:d作限:)f;h(用。() )域例内://不//BA类直类接中中可的的见ff ,如果要使

}

};

void main( )

{ B b;

b.f( );

// B 类中的 f

b.A::f( ); //A类中的f

}

《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

?即使派生类c{la中ss定Bin义:tp了zu; b与li基c A类同名但参数不同的成员

函数,基类的pu同bl名ic:函数在派生类中的作用域中也是不 void f(int);
直接可见的。例:void h( )

{ f(1);

//Ok

f( );

//

A::f( ) // Ok

}

};

void main( )

{ B b;

b.f(1); // Ok

b.f( );

//

b.A::f( ); // Ok

}

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

?定义派生类时一定要见到基类的定义,否则编译程

序无法确定派生类对象需占多大内存空间以及派生类

中对cl基as类s A成;员的访问是否合法。例:

class B : public A

{ int z;

public:

void h( )

{ g( ); } //Error没有g的原型

};

……

{ B b;

// Error无法确定 b 所需空间

}
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

?如果在派生类中没有显式说明,基类的友元不是派

生c{la类ss的Ain友t x元,y;; 如果基类是另一个类的友元,而该类没

有pu显bl式ic:说明,则派生类也不是该类的友元。

?派生vv类ooii不dd gf能(())直;{…接x访…问};基类的私有成员,必须要通过

基}; 类的非私有成员函数来访问基类的私有成员。例:

class B : public A

{ int z;

public:

void h( )

{…x… //Error, x为基类的私有成员

g( ); }

//Ok, 通过函数g访问基类的私有成员x

};

《C++程序设计 》网 络 教 学

电子与信息工程系

classCAOMPUTER SCIENCE & TECHNOLOGY DEPARTMENT { protected:
在派生i类nt x中,y对; 基类成员的访问

public:

?一个v类oid有f(两); 种用户:实例用户和派生类。例:

?}c;la一ss个B :类pu存bl在ic A两个/对/派c外l生as接类s A口

{

……

{

?给v实oi例d 用h()户使用:由类的…pu…blic成员构成

?提{}供……f给( x)y派;……生类使///用/// OOO}c{:;lkkka由ss类B的: ppuubblliicc和Aprotecte/d/派成生员构类成

};

……

void f( )

//实};例用户

{

A a;

void f( )

//实例用户

…a.x… …a.y… a.f( );

//{Error A a;

// Error
//}Ok

……

};
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
?类的聚集:一个类只有一个接口,即类的实例用户 接口。例:以继承和聚集实现的代码重用。

class A

{

……

public :

void f();

void g();

};

class B : public A

{ …… public :
void h();
…… };

class B { ……
A a; public :
void f(){a.f();} void g(){a.g();} void h(); …… };

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
?继承实现子类型,派生类可以看成基类的子类型。

?子类型的作用:发给基类对象的消息也能发给派生

类对象,以及基类的对象标识可以标识派生类对象。

class A

{

……

public :

void f();

};

class B : public A

{ …… public :
void g(); …… };

以下操作合法:

A a,*p; B b,*q;

用b去改变a的状态,属于b 但不属于a的成员将被忽略

b.f();

a=b; p=&b;

A类指针p指向B类对象b

以下操作非法:

b=a; //Error b有不确定的成员数据

q=&a; //Error

《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

例11.1 访问公有基类的成员。

Class Student

//声明基类

{public:

//基类公用成员

void get_value( )

{cin>>num>>name>>sex;}

void display( )

{cout<<″ num: ″<<num<<endl;

cout<<″ name: ″<<name<<endl;

cout<<″ sex: ″<<sex<<endl;}

private :

//基类私有成员

int num;

char name[8];

char sex;

};

class Student1: public Student //以public方式声明派生类

Student1

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
{public: void display_1( )
{co结ut论<<:″ num: ″<<num<<endl;
cout<<″ name: ″<<name<<endl; //引用基类的私有成员,错误
c?ou由t<<于″基sex类: ″的<<私sex有<<成en员dl;对派生类来说是不可访问的, c因out此<<在″ a派ge生: ″类<<中age的<<deinsdpl;la//引y_用1派函生数类中的直私接有引成员用,基正类确 c的out私<<有″ a数dd据re成ss:员″<n<uadmdr,<<nenadml;}e/和/引s用e派x是生不类允私有许成的员。,正确 pr?iv只ate能: 通过基类的公用成员函数来引用基类的私有 数i据nt a成ge员; 。
char addr[12]; };
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
可以将派生类Student1的声明改为: class Student1: public Student //以public方式声明派生类 Student1 {public: void display_1( ) { cout<<″ age: ″<<age<<endl; //派生类的私有成员,正确
cout<<″ address: ″<<addr<<endl; //派生类私有成员,正确 } void get_value_1( ) { cin>>age>>addr;} private:
int age; string addr; };
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

可以这样写main函数: void get_value_1( )

int main( )

{get_value( ) ; Cin>>age>>addr;}

{ Student1 stud;

stud. get_value ( );

stud. get_value_1 ( );

stud.display( ); void display_1( )

stud.display_1(); {display( ) ;

return 0; }

cout<<“ age: “<<age<<endl; cout<<“ address: “<<addr<<endl;}

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.4.2 私有继承
private继承: ?基类的public成员, 在派生类中成为private 成员 ?基类的protected成员, 在派生类中成为private成员 ?基类的private成员, 在派生类中成为不可直接使用的成员

表11.1私有基类在派生类中的访问属性

私有基类的成员

私有成员 公有成员 保护成员

在私有派生类中的访问属性 不可访问 私有

私有

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
图11.7表示了各成员在派生类中的访问属性。
《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

例11.2 将例11.1中的公用继承方式改为用私有继承方 式(基类Student不改)。

可以写出私有派生类如下: class Student1: private Student//用私有继承方式声明 {public:

void display_1( ) {

cout<<“age: “<<age<<endl; int main( )

cout<<“address: “<<addr<<endl{;Student1 stud1;

}

stud1.display(); //错误

private:

stud1.display_1( ); //正确

int age;

stud1.age=18; //错误

char addr[12];

return 0;

};

}

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
结论:
(1) 不能通过派生类对象(如stud1)引用从私有基 类继承过来的任何成员。 (2) 派生类的成员函数不能访问私有基类的私有成 员,但可以访问私有基类的公用成员。
在派生类外不能通过派生类对象调用私有基类 的公用成员函数,但可以通过派生类的成员函数调 用私有基类的公用成员函数(此时它是派生类中的 私有成员函数,可以被派生类的任何成员函数调 用)。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
可将上面的私有派生类的成员函数定义改写为 void display_1( ) {display(); //调用基类的公用成员函数 cout<<″age: ″<<age<<endl; //派生类的私有数据成员 cout<<″address: ″<<addr<<endl;} //派生类的私有数据成员 main函数可改写为 int main( ) {Student1 stud1; stud1.display_1( ); //Student1类的公用函数 return 0;}
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
由于私有派生类限制太多,使用不方便,一 般不常使用。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.4.3 保护成员和保护继承

protected 继承:

?基类的public成员, 在派生类中成为protected 成员

?基类的protected成员, 在派生类中成为protected 成员

?基类的private成员, 在派生类中成为不可直接使用的成员

表11.3基类成员在派生类中的访问属性

基类中的成员 在公有派生类 在私有派生类 在保护派生类

私有成员

不可访问

不可访问

不可访问

公有成员

公有

私有

保护

保护成员

保护

私有

保护

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
保护成员可以被派生类的成员函数引用。但不能被 实例函数引用。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

例:

对派生类:

class B1

class B2: public B1

{

{

int a;

public:

void f( ){cout<<a; } void df( ){

public:

cout<<a; //

int b;

cout<<b;

void g( ){cout<<a; } cout<<c;

protected:

f( ); //

int c;

g( );

void k( ){cout<<a; } k( );

};

}

};

对实例函数 void main( ) {
B1 b1; cout<<b1.a;// cout<<b1.b; cout<<b1.c; // b1.f( ); // b1.g( ); b1.k( ); // }

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
(1) 在派生类中,成员有4种不同的访问属性:

表11.4派生类中的成员的访问属性

派生类中的成员 公用的成员 受保护的成员 私有的成员 不可访问的成员

在派生类中 在派生类外

可以

可以

可以

不可以

可以

不可以

不可以

不可以

在下层公用派生类中 可以 可以 不可以 不可以

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

例11.3 在派生类中引用保voi护d S成tu员den。t::display( )

#include <iostream> {cout<<″num: ″<<num<<endl;

#include <string>

cout<<″name: ″<<name<<endl;

using namespace std; cout<<″sex: ″<<sex<<endl;

class Student

}

{public:

class Student1: protected Student

void display( ); {public:

protected :

void display1( );

int num;

private:

string name;

int age;

char sex;

char addr [12];

};

};

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
void Student1::display1( ) //定义派生类公用成员函数 {cout<<″num: ″<<num<<endl; //基类的保护成员,合法 cout<<″name: ″<<name<<endl; cout<<″sex: ″<<sex<<endl; cout<<″age: ″<<age<<endl; //派生类的私有成员,合法 cout<<″address: ″<<addr<<endl;
} int main( ) {Student1 stud1; stud1.display1( ); stud1.num=10023; // 错误,外界不能访问保护成员 return 0; }
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
在派生类的成员函数中引用基类的保护成员是合法的。 保护成员和私有成员不同之处,在于把保护成员的访 问范围扩展到派生类中。 注意: 在程序中通过派生类Student1的对象stud1的公用成 员函数display1去访问基类的保护成员num.name和 sex,不要误认为可以通过派生类对象名去访问基类 的保护成员。 私有继承和保护继承方式很容易搞错,一般不常用。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.4.4 多级派生时的访问属性

如果有图11.9所示的派生关系: 类A为 基类,类B是类A的派生类,类C是类B的 派生类,则类C也是类A的派生类。类B 称为类A的直接派生类,类C称为类A的 间接派生类。类A是类B的直接基类,是 类C的间接基类。在多级派生的情况下, 各成员的访问属性仍按以上原则确定。

图11.9

《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

例11.4 多级派c生la访ss 问B:属pu性bl。ic A

class A {public:
int i;

{public: void f3( ); protected: void f4( );

protected: private:

void f2( ); int m;

int j; private:
int k;

}; class C: protected B {public: void f5( );

};

private:

int n;

};

class B: { public:
void f3( ); int i; protected: void f4( ); void f2( ); int j;
private:
int m; };

class C: { public:
void f5( ); protected:
void f4( ); void f2( ); int j; void f3( ); int i;
private:
int n; };

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
?在派生类中是不能访问基类的私有成员的,私有 成员只能被本类的成员函数所访问,毕竟派生类与 基类不是同一个类。
?在多级派生时: 常用的是公用继承!
?采用公用继承方式,那么直到最后一级派生类都能访问 基类的公用成员和保护成员。 ?采用私有继承方式,经过若干次派生之后,基类的所有 的成员已经变成不可访问的了。 ?采用保护继承方式,在派生类外是无法访问派生类中的 任何成员的。经过多次派生后,很难记住哪些成员可以访 问,哪些成员不能访问。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.5 派生类的构造函数和析构函数
用户在声明类时可以不定义构造函数,系统会自 动设置一个默认的构造函数。
在设计派生类的构造函数时,希望在执行派生类 的构造函数时,使派生类的数据成员和基类的数据 成员同时都被初始化。
解决这个问题的思路是: 在执行派生类的构造函 数时,调用基类的构造函数。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.5.1 简单的派生类的构造函数

例11.5 简单的派生类的构造函数。

#include <iostream>

#include<string>

using namespace std;

class Student

//声明基类Student

{public:

Student(int n,string nam,char s) //基类构造函数

{num=n; name=nam; sex=s; }

~Student( ){ }

//基类析构函数

protected:

//保护部分

int num; string name;

char sex ;

};

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
class Student1: public Student {public:
Student1(int n,string nam, char s, int a,string ad): Student(n,nam,s) { age=a; addr=ad; }

调用基类构造函数

派生类新增的数据 成员初始化

void show( )

{cout<<″num: ″<<num<<endl;

cout<<″name: ″<<name<<endl;

cout<<″sex: ″<<sex<<endl;

cout<<″age: ″<<age<<endl;

cout<<″address: ″<<addr<<endl;

}

~Student1( ){ }

private:

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

int age;

string addr;

};

运行结果为

int main( )

num: 10010

R{Sotaudd,Senhta1ngsthuadi″1()1; 0010,″Wnsaegaaxemn::gef1-:9lWi″,a′fn′g,1-9li,″115 Beijing

Student1 stud2(10011,″Zahdadnrge-sfsu:n1″1,5′mB′e,2ij1in,″g2R13oaSdh,aSnhgahnagihai

Road,Beijing″);

num: 10011

stud1.show( );

name: Zhang-fun

stud2.show( );

sex: m

return 0; }

age: 21 address: 213 Shanghai Road,Beijing

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
派生类构造函数的一般形式为: 派生类构造函数名(总参数表列):基类构造函数名(参数表列) {派生类中新增数据成员初始化语句} 例如:
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
派生类构造函数在类外定义,而在类体中函数的声 明:
Student1(int n,string nam,char s,int a, string ad);
在类的外面定义派生类构造函数:
Student1∷Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s) {age=a; addr=ad;}
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
注意: ?在类中对派生类构造函数声明时,不包括基类构 造函数名及其参数表列(即Student(n,nam,s))。 ?调用基类构造函数时的实参是从派生类构造函数 总参数表中得到的,也可以不从派生类构造函数总 参数表中传递过来,而直接使用常量或全局变量。 例:
常量
Student1(string nam,char s,int a,string ad): Student(10010,nam,s) {age=a; addr=ad;}
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
?利用初始化表对构造函数的数据成员初始化 Student1(int n, string nam,char s,int a, string ad): Student(n,nam,s),age(a),addr(ad){}
?在建立一个派生类对象时,执行构造函数的顺序是:
?派生类构造函数先调用基类构造函数; ?再执行派生类构造函数本身(即派生类构造函数的函数体)。
?在派生类对象释放时,执行析构函数的顺序是:
?先执行派生类析构函数; ?再执行其基类析构函数。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.5.2 有子对象的派生类的构造函数
类的数据成员中还可以包含类对象。 例: Student s1; // s1是Student类的对象 s1就是类对象中的内嵌对象,称为子对象 (subobject),即对象中的对象。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
例11.6 包含子对象的派生类的构造函数。 #include <iostream> #include <string> using namespace std; class Student {public:
Student(int n, string nam ) {num=n; name=nam; } void display( ) {cout<<″num:″<<num<<endl<<″name:″<<name<<e ndl;} protected: int num; string name; }; class Student1: public Student
《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

{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( )

2、子类构造函数

{cout<<″This student is:″<<endl;

display(); cout<<″age: ″<<age<<endl;

1、基类构造函数

cout<<″address: ″<<addr<<endl;

} void show_monitor( )

3、派生类构造函数

{cout<<endl<<″Class monitor is:″<<endl;

monitor.display( );

//调用基类成员函数

}

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

private:

Student monitor;

//定义子对象(班长)

int age;

i{BnS}esst;tsittutjmuuridddniaen11gingn..Rsst(hh1ao)doosawwdtdur(_,d;Sm)1h;(oa1nn0ig0toh1ra0(i,)″″;)W; an运Tnnaaggduah-e行dmmil:sir″e:时1es:/,s9t11/usW输的00:d010a出e输110nn051g子t出,B″-ilsL对ei如:iij-象i下snug的:nR″数,o1a据9d,″,S1h1a5nghai

return 0; }

Class monitor is: num:10001

name:Li-sun

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
定义派生类构造函数的一般形式为: 派生类构造函数名(总参数表列): 基类构造函数名(参数表 列),子对象名(参数表列)
{派生类中新增数据成员初始化语句} ?总参数表列中的参数,应当包括基类构造函数、子对象的参 数表列中的参数和派生类中新增数据成员的参数。 ?基类构造函数和子对象的次序可以是任意的。 ?派生类构造函数的任务: 对基类、子对象和派生类数据成员 做初始化。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
执行派生类构造函数的顺序是: ?调用基类构造函数,对基类数据成员初始化; ?调用子对象构造函数,对子对象数据成员初始化; ?再执行派生类构造函数本身,对派生类数据成员 初始化。
《C++程序设计 》网 络 教 学

电子与信息工程系
11.C5OM.P例3UTE1多R1S.C7层IE多NC派E级&生派TEC时生HNO的情LO况G构Y 下D造EP派A函RT生M数E类NT 的构造函数。
#include <iostream> 一续个派类生#uis不,ninc仅形lgun可成dae以派m<se派t生srpi生的nagc出层>e s一次td个结; 派构生。类,派生类还可以继
class Student {public:
Student(int n, string nam ) {num=n; name=nam; } void display( ) {cout<<″num:″<<num<<endl; cout<<″name:″<<name<<endl; } protected: int num; string name; };
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
class Student1: public Student {public:
Student1(int n,string nam,int a):Student(n,nam) {age=a; }
void show( ) {
display( ); cout<<″age: ″<<age<<endl; } private: int age; }; class Student2:public Student1 //声明间接公用派生类 {public:
《C++程序设计 》网 络 教 学

电子与信息工程系

//C下OMP面UT是ER S间CIE接NCE派& 生TEC类HN构OLO造GY 函DEP数ARTMENT

Student2(int n, string nam,int a,int s):

Student1(n,nam,a)

{score=s;}

void show_all( ) {schoouwt<(<)″; score:″<<score<<//e输nd运出l;行nu时m的、输na出m如e和下a:ge

} private:

num:10010

int score; };

name:Li

int main( )

age:17

{ Student2 stud(10010,″Li″,17, 89);

stud.show_all( );

score:89

return 0;

}

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
其派生关系如下图。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
基类和两个派生类的构造函数的写法: ?基类的构造函数首部:
Student(int n, string nam) ?派生类Student1的构造函数首部:
Student1(int n, string nam[],int a):Student(n,nam) ?派生类Student2的构造函数首部:
Student2(int n, string nam,int a,int s):Student1(n,nam,a) ?初始化的顺序是:
?先初始化基类的数据成员num和name。 ?再初始化Student1的数据成员age。 ?最后再初始化Student2的数据成员score。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.5.4 派生类构造函数的特殊形式
使用派生类构造函数时,有以下特殊形式: ?当不需要对派生类新增的成员进行任何初始化操作 时,派生类构造函数的函数体可以为空,即构造函数 是空函数,如:
Student1(int n, string nam,int n1, string nam1):
Student(n,nam), monitor (n1,nam1) { } ?如果基类中没有定义构造函数,或定义了没有参数 的构造函数,那么在定义派生类构造函数时可不写基 类构造函数。调用派生类构造函数时系统会自动首先 调用基类的默认构造函数。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
?如果基类和子对象类型都没有定义带参数 的构造函数,而且也不需对派生类自己的数 据成员初始化,则可以不必显式地定义派生 类构造函数。在建立派生类对象时,系统会 自动调用派生类的默认构造函数,并在执行 派生类默认构造函数的过程中,调用基类的 默认构造函数和子对象类型默认构造函数。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
?如果在基类中既定义无参的构造函数,又 定义了有参的构造函数(构造函数重载),则 在定义派生类构造函数时,既可以包含基类 构造函数及其参数,也可以不包含基类构造 函数。在调用派生类构造函数时,根据构造 函数的内容决定调用基类的有参的构造函数 还是无参的构造函数。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.5.5 派生类的析构函数
?对派生类中所增加的成员进行清理工作, 需要定 义自己的析构函数。 ?基类的清理工作仍然由基类的析构函数负责。 ?在执行派生类的析构函数时,系统会自动调用基 类的析构函数和子对象的析构函数,对基类和子对 象进行清理。 ? 调用的顺序与构造函数正好相反:
?先执行派生类自己的析构函数; ?然后调用子对象的析构函数; ?最后调用基类的析构函数。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.6 多重继承
多重继承:派生类同时继承多个基类。
11.6.1 声明多重继承的方法
如果已声明了类A、类B和类C,可以声明多重继承的 派生类D:
class D: public A, private B, protected C {类D新增加的成员}
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
D是多重继承的派生类,它以公用继承方式继承A 类,以私有继承方式继承B类,以保护继承方式继 承C类。D按不同的继承方式的规则继承A,B,C的 属性,确定各基类的成员在派生类中的访问权限。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.6.2 多重继承派生类的构造函数
多重继承派生类的构造函数形式。如:
派生类构造函数名(总参数表列): 基类1构造函数(参 数表列), 基类2构造函数(参数表列), 基类3构造函数 (参数表列) {派生类中新增数成员据成员初始化语句}
?各基类的排列顺序任意。 ?派生类构造函数的执行顺序同样为: 先调用基类的构造函 数,再执行派生类构造函数的函数体。 ?调用基类构造函数的顺序是按照声明派生类时基类出现 的顺序。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

#例inc1lu1d.e8<i声ost明rea一m个> 教师(Teacher)类和一个学生

#u(isniSnctlguunddaeem<nestts)rpi类ancge,>st用d; 多重继承的方式声明一个研究生

c(laGsrs aTdeaucahteer)派生类。教师类中包括数据成员

{npaumbeli(c:姓名)、age(年龄)、title(职称)。学生类中
Teacher(string nam,int a, string t)
包{n括am数e=据na成m;员naagmee=a1;(姓t名itle)=、t;}age(性别)、score(成

绩vo)id。d在isp定lay义( )派生类对象时给出初始化的数据,然

后{输ccoouu出tt<<这<<″″些naagm数e″e<据:″<<a。g<en<a<meen<d<l;endl;

cout<<″title:″<<title<<endl; }

protected:

string name; int age; string title;

//职称

};

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

class Student {public:
Student(char nam[],char s,float sco) {strcpy(name1,nam); sex=s; score=sco;}
void display1( ) {cout<<″name:″<<name1<<endl; cout<<″sex:″<<sex<<endl; cout<<″score:″<<score<<endl; } protected: char name1[8]; char sex; float score; };

//成绩

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

class Graduate:public Teacher,public Student

{public:

Graduate(char 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:″<<name<<endl; 如果Teacher和Student类中

cout<<″age:″<<age<<endl;

都是name,会出现二义性。

cout<<″sex:″<<sex<<endl;

正确作法可加限定:

cout<<″score:″<<score<<endl; Teacher::name

cout<<″title:″<<title<<endl;

cout<<″wages:″<<wage<<endl;

}

private:

float wage;

//工资

};

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

int main( )

{Graduate grad1(“Wang-li”,24,’f’,

“assistant”,89.5,1234.5); 程序运行结果如下:

grad1.show( );

name: Wang-li

return 0;

age: 24

}

sex:f

score: 89.5

在两个基类中分别用name和ntaitmlee:1a来ss代ist表an姓ce名,其实这

是同一个人的名字,从Graduwaatgee类s:的12构34造.5函数中可以看

到总参数表中的参数nam分别传递给两个基类的构造函

数,作为基类构造函数的实参。

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.6.3 多重继承引起的二义性问题

在多重继承中,当多个基类中包含同名成员时,它们在派 生类中就会出现名冲突问题。例:

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();

};

void main(){ C c1;

同名冲突

c1.a=3;

c1. display( );

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
(1) 两个基类有同名成员。如图所示。
《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

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();

void main(){

C c1;

c1.a=3;

//二义性

c1.display(); //二义性

vo}id main(){

C c1;

c1.A::a=3;

//正确

c1. A:: display(); //正确

void }show(){

A::a=3;

//不必加对象名

A::display(); //不必加对象名

}

};

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
两个基类有同名成员的表示:
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

(2) 两个基类和派生类三者都有同

名成员。将上面的C类声明改为

class C :public A,public B

{int a; void display(); };

基类中的同名 成员被覆盖

void main(){

C c1;

c1.a=3;

//访问C中的vo成id员main(){

c1.display( ); //访问C中的C成c1员;

}

c1.A::a=3; //访问A中的成员

c1. A:: display( ); //访问A中的成员

}

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
规则: 基类的同名成员在派生类中被屏蔽,成为“不 可见”的,或者说,派生类新增加的同名成员 覆盖了基类中的同名成员。 注意: ?不同的成员函数,只有在函数名和参数个数 相同、类型相匹配的情况下才发生同名覆盖 ?如果只有函数名相同而参数不同,不会发生 同名覆盖,而属于函数重载。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
(3) 如果类A和类B是从同一个基类派生的,如图所示。
《C++程序设计 》网 络 教 学

电子与信息工程系

clCaOsMsPNUTER SCIENCE {public:

&

TECcHlNaOsLsOGCY

D:EpPuARbTlMiEcNAT ,public

B

int a;

{public :

{couvto<id<″dAis:p:ala=y”(<) <a<<inetnda3l;;}

};

void show( ){cout<<a3<<endl;}

class A:public N

{public:

};

int a1;

int main( )

}; class B:public N { C c1;

{public:



int a2; };

}

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
派生类C中成员的情况。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

void main(){ C c1; c1.a=3; //二义性 c1.display( ); //二义性 }

void main(){ C c1; c1.N::a=3; //二义性 c1.N::display( ); //二义性 }

void main(){ C c1; c1.A::a=3; c1.A::display(); //访问类N的派生类A中的基类成员 }

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.6.4 虚基类
1. 虚基类的作用 C++提供虚基类(virtual base class)的方法,使得 在继承间接共同基类时只保留一份成员。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
?声明虚基类的一般形式为: class 派生类名: virtual 继承方式 基类名
声明为虚基类后,基类通过多条派生路径被一个派生 类继承时,该派生类只继承该基类一次。
例:将类A声明为虚基类的方法如下: class A {…}; class B :virtual public A //类A是B的虚基类 {…}; class C :virtual public A //类A是C的虚基类 {…};
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
在派生类B和C中作了上面的虚基类声明后,派生 类D中的成员如下图所示。

D类

int data; int data_b; int data_c void fun( );

基类A只被继承一次

int data_d;

void fun_d( );

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
注意: 为了保证虚基类在派生类中只继承一次,应 当在该基类的所有直接派生类中声明为虚基类。否 则仍然会出现对基类的多次继承。见下图
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

2. 虚基类的初始化

如c果las在s A虚基类中定义了带参数的构造函数,而且没

有{定A(义int默i){认} 构造函数,则在其所有派生类(包括直接
…};
派c生las或s B间:v接irt派ual生pu的bl派ic A生类)中//A,作为通B过的构虚造基函类 数的初始

化{表B(对int虚n)基:A(类n)进{ }行初始化/。/在初始化表中对虚基类初始化
…};
例c:lass C :virtual public A

{C(int n):A(n) { }

//在初始化表中对虚基类初始化

…};

class D :public B,public C

{D(int n):A(n),B(n),C(n){ } //在初始化表中对所有基类初始化

…};

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
注意: 在定义类D的构造函数时,与以往使用的方法 有所不同。 规定: 在最后的派生类中不仅要负责对其直接基类 进行初始化,还要负责对虚基类初始化。 C++编译系统只执行最后的派生类对虚基类的构造 函数的调用,而忽略虚基类的其他派生类(如类B和 类C) 对虚基类的构造函数的调用,这就保证了虚基 类的数据成员不会被多次初始化。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
3. 虚基类的简单应用举例 例一个两用沙发(SleeperSofa),既有沙发的功能, 又有床的功能,所以,它同时继承了床和沙发的特征, 因此,又称沙发床。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
多继承结构

Bed(床) Sleep()
setWeight() weight

Sofa(沙发) watchTV()
setWeight() weight

foldOut()

SleeperSofa (沙发床)

《C++程序设计 》网 络 教 学

#includ电e<子io与st信re息am工>程系 usinCgOnMPaUmTEResSCpIEaNcCeE s&tdTE;CHNOLOGY DEPARTMENT class Bed{ protected:
int weight; public:
Bed( ):weight(0){} void sleep( )const{cout<<"Sleeping...\n";} void setWeight(int i) {weight=i;} };//----------------------------------------------------------class Sofa{ protected: int weight; public: Sofa():weight(0){} void watchTV()const{cout<<"Watching TV.\n";} void setWeight(int i){weight=i;} };//-----------------------------------------------------------
《C++程序设计 》网 络 教 学

电子与信息工程系
clasCsOMSPlUeTeERpSeCrIESNoCEfa&:pTEuCbHNlOicLOBGYedDE,PpAuRTbMlEiNcT Sofa

{public:

SleeperSofa( ){ }

void foldOut( )const{cout<<"Fold out the sofa.\n";}

};//-------------------------s-s-.-s-e-t-W---e-i-g--h-t-(-2--0-)-;--/-/-E--R--R--O--R--!---二-------

int main()

义性!

{ SleeperSofa ss; ss. Sofa::setWeight(20);

ss.watchTV(); ss.foldOut();

运行结果:

ss.sleep();

Watching TV.

return 0; } //---------------------------

Fold out the sofa. Sleeping...

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
基类分解

Furniture(家具) getWeight() setWeight() weight

Furniture(家具) getWeight() setWeight() weight

Bed(床) Sleep() weight

Sofa(沙发) watchTV() weight

SleeperSofa foldOut() (沙发床)
《C++程序设计 》网 络 教 学

#inclu电d子e<与io信st息re工am程>系 usiCnOgMPnUTaEmR SeCsIEpNCaEce& sTEtdCH;NOLOGY DEPARTMENT //---------------------------------------------class Furniture {protected:
int weight; public:
Furniture():weight(0){} void setWeight(int i){weight=i;} int getWeight()const{return weight;} };//--------------------------------------------class Bed:public Furniture {public: Bed(){} void sleep()const{cout<<"Sleeping...\n";} };//----------------------------------------------
《C++程序设计 》网 络 教 学

class S电of子a:与pu信bl息ic工Fu程r系niture

{puCbOlMicPU:TER SCIENCE & TECHNOLOGY DEPARTMENT

Sofa () {}

void watchTV()const{cout<<"Watching TV.\n";}

};//--------------------------------------------------------------------

class SleeperSofa:public Bed,public Sofa

{public:

SleeperSofa():Sofa(),Bed() {}

void foldOut()const{cout<<"Fold out the sofa.\n";}

};//--------------------------------------------------------------------

int main()

{ SleeperSofa ss;

ss.setWeight(20);

//ERROR ss.setWeight

Furniture* pF=(Furniture*)&ss; //ERROR Furniture*

cout<<pF->getWeight()<<endl;

return 0;

}//-------------------------------------------------------------------------

《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

虚拟继承

Furniture(家具)

getWeight()

setWeight() weight

Bed(床) Sleep() weight

Sofa(沙发) watchTV() weight

SleeperSofa foldOut() (沙发床)
《C++程序设计 》网 络 教 学

电子与信息工程系
#inCcOlMuPdUTeE<R iSoCIsEtNrCeEa&mTE>CHNOLOGY DEPARTMENT using namespace std; //-------------------------------------class Furniture {protected:
int weight; public:
Furniture(){} void setWeight(int i){weight=i;} int getWeight()const{return weight;} };//-----------------------------------------------------class Bed:virtual public Furniture {public: Bed(){} void sleep()const{cout<<"Sleeping...\n";} };//-------------------------------------------------------
《C++程序设计 》网 络 教 学

class电S子of与a:信vi息rt工ua程l p系ublic Furniture {pCuOMbPlUiTcE:R SCIENCE & TECHNOLOGY DEPARTMENT
Sofa(){}

void watchTV()const{cout<<"Watching

TV.\n";}

};//-----------------------------------------------------------

-

class SleeperSofa:public Bed,public Sofa

{public:

构造函数按照继承顺序执行

SleeperSofa(): Furniture(), Sofa(),Bed() {}

void foldOut()const{cout<<"Fold out the

sofa.\n";}

};//-----------------------------------------------------------

int main(){

Run:

SleeperSofa ss;

20

ss.setWeight(20);
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
多继承对象构造顺序:
?任何虚拟继承的构造函数按照它们被继承的顺序构造 ?任何非虚拟继承的构造函数按照它们被继承的顺序构造 ?任何成员对象的构造函数按照它们声明的顺序构造 ?类自己的构造函数
含有虚拟继承的类的构造函数,其对象构造顺序为:
?Furniture对象; ?对象Bed,不管构造函数的初始化表是否把Bed排在Sofa后面; ?对象Sofa ; ?SleeperSofa本身部分;
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.7 基类与派生类的转换
只有公用派生类才是基类真正的子类型,它完整地 继承了基类的功能。 基类与派生类对象之间有赋值兼容关系,由于派生 类中包含从基类继承的成员,因此可以将派生类的 值赋给基类对象,在用到基类对象的时候可以用其 子类对象代替。具体表现在以下几个方面:
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

(1) 派生类对象可以向基类对象赋值。

可以用子类(即公用派生类)对象对其基类对象赋值。 如

A a1;

//定义基类A对象a1

B b1;

//定义类A的公用派生类B的对象b1

a1=b1;

//用派生类B对象b1对基类对象a1赋值

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
?子类型关系是单向的、不可逆的。B是A的子类型, 不能说A是B的子类型。 ?只能用子类对象对其基类对象赋值,而不能用基类对 象对其子类对象赋值,理由是显然的,因为基类对象 不包含派生类的成员,无法对派生类的成员赋值。 ?同一基类的不同派生类对象之间也不能赋值。 (2) 派生类对象可以替代基类对象向基类对象的引用 进行赋值或初始化。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

如已定义了基类A对象a1,可以定义a1的引用变量:

A a1;

//定义基类A对象a1

B b1;

//定义公用派生类B对象b1

A& r=a1; //定义A对象的引用变量r,引用变量r是a1的别名

A& r=b1; //定义A对象的引用变量r,用对象b1对其初始化

r=b1; //用派生类B对象b1对a1的引用变量r赋值

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
(3) 如果函数的参数是基类对象或基类对象的引用,相

应的实参可以用子类对象。

如有一函数fun:

void fun(A& r)

//形参是类A的对象的引用变量

{cout<<r.num<<endl;} //输出该引用变量的数据成员num

……

fun(b1);

在fun函数中只能输出派生类中基类成员的值。

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

(4) 派生类对象的地址可以赋给指向基类对象的指 针变量,也就是说,指向基类对象的指针变量也可 以指向派生类对象。 例:

A a1,*p1; B b1,*p2; p1=&b1; P1->disp();

//定义基类A对象a1 //定义派生类B对象b1
//用指向基类对象的指针输出数据

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
11.8 继承与组合
在一个类中可以用类对象作为数据成员,即子对象。 对象成员的类型是基类。
对象成员的类型可以是本派生类的基类,也可以是 另外一个已定义的类。在一个类中以另一个类的对 象作为数据成员的,称为类的组合(composition)。
例如:声明Professor(教授)类是Teacher(教师)类的派生类, 另有一个类BirthDate(生日),包含year,month,day等数据 成员。可以将教授生日的信息加入到Professor类的声明中。 如
《C++程序设计 》网 络 教 学

class Te电ac子he与r 信息工程系

{pubClOicM:PUTER SCIENCE & TECHNOLOGY DEP…AR…TME.NT

┆ private:

void fun1(Teacher &);

int num; string name; char sevxo;id fun2(BirthDate &);

}; class BirthDate

void main()

{ public: ┆
private:

{Professor prof1;

fun1(prof1);

//正确

}; int year;int month;int day; fun2(prof1.birthday); //正确

class Professor:public Teacher fun2(prof1);

{public:



}

//错误

private:

BirthDate birthday; }; //BirthDate类的对象作为数据成员

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
?类的组合和继承一样,是软件重用的重要方式。 组合和继承都是有效地利用已有类的资源。但二 者的概念和用法不同。继承是纵向的,组合是横 向的。 ?通过这种方法有效地组织和利用现有的类,大 大减少了工作量。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
作业: 1、1~6 2、7~10
《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

#include<iostream.h>

class A

{ int x,y;

public:

void f(){cout<<"A in f";}

void g();

};

class B : public A

{ int z;

public:

void f(int r){cout<<r<<endl;}

void h( )

{ f(1);

//Ok

f( );

//

A::f( ); }

// Ok

};

void main( )

{ B b;

b.f(1);

// Ok

b.f( ); //

b.A::f( );

// Ok

}

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
3. 虚基类的简单应用举例
例11.9 在例11.8的基础上,在Teacher类和Student类之上增加 一个共同的基类Person,如图11.25所示。作为人员的一些基本 数据都放在Person中,在Teacher类和Student类中再增加一些必 要的数据。
《C++程序设计 》网 络 教 学

电子与信息工程系

#incluCdOeM<PUioTEstRrSeCaIEmNC>E & TECHNOLOGY DEPARTMENT

#include <string>

using namespace std;

class Person

//声明公共基类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 //声明Person为公用继承虚基类

{public:

Teacher(string nam,char s,int a, string t):Person(nam,s,a)//构造函



{title=t; } protected:
string title; };

//保护成员 //职称

《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

class Student:virtual public Person //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 Student //直接基类

{public:

Graduate(string nam,char s,int a, string 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:″<<name<<endl;

cout<<″age:″<<age<<endl;

cout<<″sex:″<<sex<<endl;

cout<<″score:″<《<Csc+o+r程e<序<设en计dl》; 网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

cout<<″title:″<<title<<endl;

cout<<″wages:″<<wage<<endl;

}

private:

float wage;

//工资

};

int main( ) {Graduate grad1(″Wangli″,′f′,24,″assistant″,89.5,1234.5); grad1.show( ); return 0; }

运行结果为 name: Wang-li age:24 sex:f score:89.5 title:assistant wages:1234.5

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
可以看到: 使用多重继承时要十分小心,经常会出 现二义性问题。许多专业人员认为: 不要提倡在程 序中使用多重继承,只有在比较简单和不易出现二 义性的情况或实在必要时才使用多重继承,能用单 一继承解决的问题就不要使用多重继承。也是由于 这个原因,有些面向对象的程序设计语言(如Java, Smalltalk)并不支持多重继承。
《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

#include <iostream>

#include <string>

using namespace std;

class Student

//声明Student类

{public:

Student(int, string,float);

//声明构造函数

void display( );

//声明输出函数

private:

int num; string name; float score;};

Student::Student(int n, string nam,float s) //定义构造函数

{num=n; name=nam; score=s; }

void Student::display( )

//定义输出函数

{cout<<endl<<″num:″<<num<<endl;

cout<<″name:″<<name<<endl;

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

cout<<″score:″<<score<<endl;}

class Graduate:public Student //声明公用派生类Graduate

{public:

Graduate(int, string ,float,float);

//声明构造函数

void display( );

//声明输出函数

private:

float pay; };

//工资

Graduate::Graduate(int n, string nam,float s,float

p):Student(n,nam,s),pay(p){ }

//定义构造函数

void Graduate::display()

//定义输出函数

{Student::display();

//调用Student类的display函数

cout<<″pay=″<<pay<<endl;}

《C++程序设计 》网 络 教 学

电子与信息工程系

COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT

int main()

{Student stud1(1001,″Li″,87.5);

num:1001

Graduate grad1(2001,″Wang″,98.5,563.5); name:Li

Student *pt=&stud1;

score:87.5

pt->display( ); pt=&grad1; pt->display( ); }

//调用stud1.display函数 ////调指用针g指ra向d1g.rdaidsp1lay函数nnscuaommree::2:9w080a.15ng

很多读者会认为: 在派生类中有两个同名的display成员函数, 根据同名覆盖的规则,被调用的应当是派生类Graduate对象的 display函数,在执行Graduate::display函数过程中调用 Student::display函数,输出num,name,score,然后再输出pay 的值。事实上这种推论是错误的,先看看程序的输出结果:

《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
并没有输出pay的值。问题在于pt是指向Student 类对象的指针变量,即使让它指向了grad1,但实 际上pt指向的是grad1中从基类继承的部分。通过 指向基类对象的指针,只能访问派生类中的基类成 员,而不能访问派生类增加的成员。所以pt>display()调用的不是派生类Graduate对象所增 加的display函数,而是基类的display函数,所以 只输出研究生grad1的num,name,score3个数据。 如果想通过指针输出研究生grad1的pay,可以另 设一个指向派生类对象的指针变量ptr,使它指向 grad1,然后用ptr->display()调用派生类对象的 display函数。但这不大方便。
《C++程序设计 》网 络 教 学

电子与信息工程系
COMPUTER SCIENCE & TECHNOLOGY DEPARTMENT
通过本例可以看到: 用指向基类对象的指针变量指 向子类对象是合法的、安全的,不会出现编译上的 错误。但在应用上却不能完全满足人们的希望,人 们有时希望通过使用基类指针能够调用基类和子类 对象的成员。在下一章就要解决这个问题。办法是 使用虚函数和多态性。
《C++程序设计 》网 络 教 学


相关文档

第章继承与派生newppt课件
第章继承与派生newppt课件
第章继承与派生newppt课件
第章继承与派生newppt课件
第章继承与派生newppt课件
继承与派生newppt课件
第7章 继承与派生-优秀课件
c++课件第十一章 继承与派生名师教学资料
七年级政治上册-第一课-走进中学课件-教科版贾艳萍
电脑版