第四章 继承和类的派生

第四章 继承和类的派生
第四章 继承和类的派生

实验三继承与派生

一、实验目的和要求

1 掌握继承和派生类的概念。

2 掌握派生类对基类的继承。

3 了解多继承。

二、基本概念

C++的继承机制允许类从一个或更多的类中继承操作,允许根据需要进行更具体的定义来建立新类。类的继承就是新的类从已有类那里得到已有的特性。这个从已有类产生新类的过程我们就叫它类的派生。派生类同样也可以作为基类派生新的类,这样就形成了类的层次结构。

派生新类的过程一般要经历三个步骤:吸收基类成员、改造基类成员、添加新的成员。

所派生的新类具有下列特性:

(1)新的类可在基类所提供的基础上包含新的成员;

(2)在新的类中可隐藏基类的任何函数成员;

●定义派生类的一般语法为:

class 派生类名:继承方式基类名1,继承方式基类名2,.....继承方式基类名n {

派生类成员定义;

};

●派生类构造函数

?C++的继承就是派生类拥有全部基类的属性和服务。但是基类的构造函数和析构函

数不能被继承,在派生类中,如果对派生类新增的成员进行初始化,就必须由程序员针对实际需要加入新的构造函数。与此同时,对所有从基类继承下来的成员的初始化工作,还是由基类的构造函数完成,我们必须在派生类中对基类的构造函数所需要的参数进行设置。同样,对派生类对象的扫尾、清理工作也需要加入新的析构函数。

?在定义对象时构造函数的执行顺序是先祖先(基类),再客人(对象成员),后自己

(派生类本身)。

●派生类构造函数的一般语法规则为:

派生类名::派生类名(参数总表):基类名1(参数表1),,基类名n(参数表n),对象成员名1(对象成员参数表1),,对象成员名n(对象成员参数表n)

//派生类新增成员的初始化语句;

};

其中:

派生类名:派生类的构造函数名与类名相同。

参数表:包括初始化基类数据、新增对象数据及新增一般成员所需全部参数。

基类名:需要使用参数进行初始化的基类名及参数表。

对象成员:类对象名

派生类的析构函数

若派生类在退出其定义域前有数据需做善后工作(如释放内存等),就需定义析构函数。

若基类、成员类、派生类均有析构函数,则执行时的顺序正好与构造函数的相反,即先自己(派生类)、再客人(对象成员),后祖先(基类)。

三、程序例题

例题3.1派生类构造函数、析构函数实例(多继承、含有子对象)。

程序实现:

#include "iostream.h"

class B1 //基类B1定义,构造函数有参数{ public:

B1(int i) {cout <<"constructing B1"<< i << endl;} //B1的构造函数

~B1( ) {cout <<"destructing B1"<< endl;} //B1的析构函数};

class B2 //基类B2定义,构造函数有参数{ public:

B2(int j) {cout <<"constructing B2"<< j << endl;} //B2的构造函数

~B2( ) {cout <<"destructing B2"<< endl;} //B2的析构函数};

class B3 //基类B3定义,构造函数无参数{ public:

B3( ) {cout <<"constructing B3 *"<< endl;} //B3的构造函数

~B3( ) {cout <<"destructing B3"<< endl;} //B3的析构函数};

class C: public B2, public B1, public B3 //派生新类C定义,注意基类名的顺序{ private:

B1 memberB1;

B2 memberB2;

B3 memberB3;

Public:

C(int a, int b, int c, int d): B1(a), memberB2(d), memberB1(c), B2(b){;}

//派生类构造函数定义,注意基类名的个数与顺序

//注意成员对象名的个数与顺序

void main( )

{C obj(1, 2, 3, 4);}

程序进行结果:

该题中的主函数只是定义了一个派生类C的对象C,生成对象C时调用了派生类的构造函数。从题中可以看出:基类构造函数的调用顺序应该是按照派生类声明时的顺序,应该是先B2,再B1,再B3,而成员对象的调用顺序应该是按照成员在类中声明的顺序,应该是先B1,再B2,再B3。析构函数的执行顺序与构造函数的执行顺序相反。

例题3.2有一个简单字符串类,它包含设置字符串、取字符串长度及内容等功能。由它派生出一个具体编辑功能的编辑字符串类,该编辑类是在简单串类的基础上,支持一些高级的功能。编辑类中由于设置了一个光标,使其能支持在光标处的插入、替换、删除等编辑功能。

程序实现:

#include"iostream.h"

#include"string.h"

class String //定义简单字符串类

{ int length;

char *contents;

public:

~String( ){delete contents;} //析构函数

int GetLength( ){return length;} //取字符串长度

char *GetContents( ){return contents;} //取字符串内容

int SetCon(char *con); //置字符串,也可修改字符,重载SetCon( )

void Print( ){cout << contents << endl;} //输出字符串

};

//定义编辑字符串类

class EditString: public String

{ int cursor; //光标位置

public:

int GetCursor( ){return cursor;} //取当前光标位置

void MoveCur(int num){cursor = num;} //移动光标

int InStr(String *newtext); //在光标所在位置插入新字符串

int ReplStr(String *newtext); //在光标所在位置用新字符串替换

void DelStr(int num); //在光标所在位置开始删除num个字符};

int String::SetCon(char *con)

if(!contents) delete contents; //若字符串已有内容,则行删除

contents = new char[length+1]; //为字符串分配存储

strcpy(contents, con); //字符串赋值

return length;

}

int EditString::InStr(String *newtext)

{ int el, k, sl;

char *sp, *ep;

el = newtext-> GetLength( );

ep = newtext-> GetContents( );

sl = GetLength( );

sp = GetContents( );

char *news = new char[el+sl+1];

for(int i = 0; i < cursor; i++)

news[i] = sp[i]; //将当前光标之前的内容赋值给news k = i;

for(int j = 0; j < el; i++,j++)

news[i] = ep[j];

cursor = i;

for(j = k; j < sl; j++, i++)

news[i] = sp[j];

news[i] ='\0';

SetCon(news);

delete news;

return cursor;

}

int EditString::ReplStr(String *newtext)

{ int el, sl;

char *ep, *news;

el = newtext-> GetLength( );

ep = newtext-> GetContents( );

sl = GetLength( );

news = new char[sl > el+cursor?sl+1: el+cursor+1];

news = GetContents( );

for(int i = cursor, j = 0; i < el+cursor; j++, i++)

news[i] = ep[j];

if(sl < el+cursor) news[i] ='\0';

cursor = i;

SetCon(news);

delete news;

return cursor;

}

void EditString::DelStr(int num)

{ int sl;

char *sp;

sl = GetLength( );

for(int i = cursor; i < sl; i++)

sp[i] = sp[i+num];

sp[i] ='\0';

}

void main( )

{ String s1; //定义简单字符串对象s1

EditString s2; //定义编辑字符串类对象s2

char *cp;

s1.SetCon("字符串: Programming"); //为s1赋值

cout <<"s1内容: ";

s1.Print( );

cp = s1.GetContents( ); //将对象s1的内容取出赋给cp

es = s2.SetCon(cp); //将cp内容赋给es

cout <<"s2内容: ";

s2.Print( ); //输出es内容

s2.MoveCur(8); //移动光标位置到8

s1.SetCon("Windows"); //修改s1对象的字符串内容

s2.InStr(&s1); //将s1对象的内容插入到es对象中

cout <<"\ns1内容: "; s1.Print( );

cout <<"插入后结果: "<< endl;

s2.Print( ); //显示es内容

s2.MoveCur(15); //移动光标位置到15

s2.DelStr(8); //在当前光标处删除8 个字符

cout <<"\n删除后结果: "<< endl;

s2.Print( );

s1.SetCon("TTT"); //修改s1对象的字符串内容

s2.ReplStr(&s1);

cout <<"\ns1内容: "; s1.Print( );

cout <<"替换后的结果: ";

s2.Print( );

}

程序运行结果:

例题3.3对于一个n元线性方程组,给出方程各项系数及方程组右边数据,试用高斯消元法求线性方程组的解。例:设一3元线性方程组为:

?????=++=++=++4.154340549.2732210

210210x x x x x x x x x 解:因为用高斯消元法,需要进行矩阵运算,所以要先定义一个矩阵类Matrix 作为基类,再定义一个方程组类Linequ 作为Matrix 类的派生类。具体设计如下:

(1)基类:矩阵类Matrix

1)数据成员包括数组的首地址和n ;

2)成员函数有:构造函数用于动态分配内存,SetMatrix( )函数用于矩阵赋值,PrintM( )函数用于显示矩阵。

(2)派生类:方程组类Linequ

1)数据成员除了继承过来用于存放系数矩阵A 的成员之外,还包括存放解向量x 的solu 和存放方程右端向量b 的sumr 。

2)成员函数有:方程组设置SetLinequ( )函数,显示方程组系数函数PrintL( ),求解函数Solve( ),显示结果函数DispR( )。

程序实现:

#include "iostream.h"

#include "math.h"

class Matrix //定义基类矩阵Matrix

{public:

Matrix(int ind = 2); //构造函数

~Matrix( ); //析构函数

void SetMatrix(double *mat); //矩阵赋值

void PrintM( ) ; //显示矩阵

protected: //保护数据成员

int index; //矩阵的维数

double *MatrixA; //矩阵存入数组首地址

};

//定义线性方程类为矩阵的公有派生类

class Linequ: public Matrix

{ double *sumr; //方程右端项数据

double *solu; //方程的解

public:

Linequ(int dirm = 2);

~Linequ( );

void SetLinequ(double *a, double *b); //方程赋值

void PrintL( ); //显示方程

int Solve( ); //全选主元高斯消元法求解方程 void DispR( ); //显示方程的解

};

//基类成员函数的实现

Matrix::Matrix(int ind) //Matrix 类构造函数

{ index = ind; //保护数据赋值

MatrixA = new double[index*index]; //动态分配内存

}

{ delete [ ] MatrixA; } //释放内存

void Matrix::SetMatrix(double *mat) //设置矩阵

{ for(int i = 0; i < index*index; i++)

*(MatrixA+i)= mat[i]; //矩阵成员赋初值

}

void Matrix::PrintM( )

{ cout <<"矩阵为: "<< endl;

for(int i = 0; i < index; i++)

for(int j = 0; j < index; j++)

cout <<*(MatrixA+i*index+j)<< " ";

}

//派生类Linequ的成员函数的实现

Linequ::Linequ(int dims):Matrix(dims) //派生类Linequ的构造函数{ //使用参数调用基类构造函数sumr = new double[dims]; //动态分配内存

solu = new double[dims];

}

Linequ::~Linequ( ) //派生类析构函数

{ delete [ ]sumr;

delete [ ]solu;;

}

void Linequ::SetLinequ(double *a, double *b) //设置线性方程组{ SetMatrix(a); //调用基类函数for(int i = 0; i < index; i++)

sumr[i] = b[i];

}

void Linequ::PrintL( ) //显示线性方程组{ cout <<"线性方程: "<< endl;

for(int i = 0; i < index; i++)

{ for(int j = 0; j < index; j++)

cout <<*(MatrixA+i*index+j)<< " ";

cout <<" "<< sumr[i] << endl;

}

}

void Linequ::DispR( ) //显示方程的解{ cout <<"结果: "<< endl;

for(int i = 0; i < index; i++)

cout <<"X["<< i <<"]="<< solu[i] << endl;

}

int Linequ::Solve( ) //高斯消元法解方程{ int *js, l, k, i, j, s, p, q;

double d, t;

js = new int[index];

l =1;

for(k = 0; k <= index-2; k++) //消元过程

for(i = k; i <= index-1; i++)

for(j = k; j <= index-1; j++)

{ t = fabs(MatrixA[i*index+j]);

if(t > d) {d = t; js[k] = j; is = i;}

}

if(d+1.0==1.0) l = 0;

else

{ if(js[k]!= k)

for(i = 0; i <= index-1; i++)

{ p = i*index+k;

q = i*index+js[k];

t = MatrixA[p];

MatrixA[p] = MatrixA[q];

MatrixA[q] = t;

}

if(is!= k)

{ for(j = k; j <= index-1; j++)

{ p = k*index+j;

q = is*index+j;

t = MatrixA[p];

MatrixA[p] = MatrixA[q];

MatrixA[q] = t;

}

t = sumr[k]; sumr[k] = sumr[is]; sumr[is] = t;

}

}

if(l = = 0)

{ delete[ ]js;

cout <<"fail"<

return(0);

}

d = MatrixA[k*index+k];

for(j = k+1; j <= index-1; j++)

{ p = k*index+j;

MatrixA[p] = MatrixA[p]/d;

}

sumr[k] = sumr[k]/d;

for(i = k+1; i <= index-1; i++)

{ for(j = k+1; j <= index-1; j++)

{ p = i*index+j;

MatrixA[p] = MatrixA[p]-MatrixA[i*index+k]*MatrixA[k*index+j];

}

sumr[i] = sumr[i]-MatrixA[i*index+k]*sumr[k];

}

}

if(fabs(d)+1.0 = = 1.0)

{ delete[] js;

cout <<"fail"<< endl;

return(0);

}

solu[index-1] = sumr[index-1]/d; //回代过程

for(i = index-2; i >= 0; i--)

{ t = 0.0;

for(j = i+1; j <= index-1; j++)

t = t+MatrixA[i*index+j]*solu[j];

solu[i] = sumr[i]-t;

}

js[index-1] = index-1;

for(k = index-1; k >= 0; k--)

if(js[k]!= k)

{ t = solu[k];

solu[k] = solu[js[k]];

solu[js[k]] = t;

}

delete[ ] js;

return(1);

}

void main( )

{ double a[ ] = {1, 2, 3, 4, 0, 5, -4, 3, -1}; //方程系数矩阵

double b[3] = {27.9, 40, -15.1}; //方程右端项数据

Linequ equ1(3); //定义一个三元方程组对象

equ1.SetLinequ(a, b); //设置方程组

equ1.PrintL( ); //输出方程组

if(equ1.Solve( )) //求解方程组

equ1.DispR( ); //输出方程组的解

else

cout <<"fail"<< endl;

}

程序运行结果:

例题3.4有一个图书馆类,内有数据成员“借书卡号”和“书名”;还有一个学生类,内有数据成员“姓名”。试设计一个读者类,它是图书馆类和学生类的派生类,它继承了它们所有数据成员和成员函数,在此基础上要添加一个查找函数,可根据输入的读者姓名,显示出该读者相关信息。

(1)定义一个图书馆类Library,其主要功能是记录读者的借书卡号和书名;

(2)定义一个学生类Student,它的主要作用是记录读者的姓名;

(3)定义一个通过读者类Reader,它是Library和Student的派生类,它除了继承这两个类的数据成员和的成员,还定义自己的数据成员,通过该类可以了解到读者的上述所有信息,还记录了读者的借还书日期。

三个类的关系如图4.1所示。

图4.1 派生类与基类的关系

程序实现:

#include "iostream.h"

#include "time.h"

#include "string.h"

class Library //定义图书馆类

{protected:

char Card[10]; //读者卡号

char Book[30]; //书名

public:

void SetLib( );

void DispLib( )

{ cout <<"读者卡号: "<< Card <

cout <<"书名: "<< Book << endl;

}

};

class Student

{protected:

char Name[10]; //姓名

public:

void SetStu( )

{ cout <<"请输入姓名: ";

cin >> Name;

}

void DispStu( )

{ cout <<"姓名: "<< Name << endl; }

};

//定义读者类,它是图书馆类和学生的派生类

class Reader: public Student, public Library

{ char datebuf[9]; //存放当前日期int Date1[3]; //存放借书日期

int Date2[3]; //存放还书日期public:

Reader( );

void SetBor( ); //输入读者信息

int Find(char n[ ]); //查找函数

void Display( ); //显示数据

};

Reader::Reader( )

{ _strdate(datebuf); //获取当前日期//将字符型日期转换为整型,作为借书日期

Date1[0] =(datebuf[0]- '0')*10+datebuf[1]- '0'+2000;

Date1[1] =(datebuf[6]- '0')*10+datebuf[7]- '0';

Date1[2] ='datebuf[3]- '0' )*10+datebuf[4]- '0';

//计算还书日期,忽略每月天数

Date2[2] = Date1[2];

if((Date1[1]+1)> 12)

{ Date2[1] =(Date1[1]+1)%12;

Date2[0] =Date1[0]+1;

}

else

{ Date2[0] = Date1[0];

Date2[1] = Date1[1]+1;

}

}

void Library::SetLib( )

{ cout <<"请输入借书卡号: ";

cin >> Card;

cout <<"请输入书名: ";

cin >> Book;

}

void Reader::SetBor( )

{ SetStu( );

SetLib( );

}

int Reader::Find(char *n)

else return 0;

}

void Reader::Display( )

{ cout <<"\n读者信息: "<< endl;

DispStu( );

DispLib( );

cout <<"借书日期: "<

cout <<"还书日期: "<< Date2[0] <<"年"<< Date2[1] <<"月"<< Date2[2] <<"日"<< endl;

}

void main( )

{ Reader *stu; //定义读者类指针

char n[30];

int i, m;

cout <<"请输入人数: ";

cin >> m;

stu = new Reader[m];

for(i = 0; i < m; i++)

{ cout <<"\n请输入第"<< i+1 <<"人信息: "<< endl;

stu[i].SetBor( );

}

for(i = 0; i < m; i++)

stu[i].Display( );

cout <<"\n请输入要查找人姓名: ";

cin >> n;

for(i = 0; i < m; i++)

{ if(stu[i].Find(n))

{ stu[i].Display( );

cout << endl;

break;

}

}

if(i = = m)

cout <<"没此人信息!"<< endl;

}

程序运行结果:

四、实验题目

1 编写一个程序,定义一个汽车类vehicle,它具有一个需传递参数的构造函数,类中的

数据成员包括:车轮个数和车的重量,它们都放在保护段中;定义轿车类car是汽车类vehicle的私有派生类,其中包含载人数;再定义卡车类truck是汽车类vehicle的私有派生类,其中包含载人数和载重量。每个类都有相应的数据输出。

2 编写一个程序,其中有一个书类book,该类的数据成员包括:书号、书名、定价、出

版社及出版时间;有一个作者类author,该类的数据成员包括:姓名、年龄和写作时间,每个类都有相应的数据输入、输出。以此两个类为基类,派生出图书查询卡card,并增加一个数据成员表示书籍系统名称,以及一个可以显示系统名称、书名、作者、作者年龄、出版时间、出版社和定价等数据的函数。

3 设计一个大学的类系统,学校中有学生、教师,每种人员都有自己的特性,他们之间

有相同的地方(以person类为基类,有姓名、编号),又有各自不同的特性(学生:专业、平均成绩;教师:职称、工资)。利用继承机制定义这个系统中的各个类,要求输入姓名等信息后再将这些信息输出。

C++继承与派生类习题

第九章继承与派生类 9.2 典型例题分析与解答 例题1:下列对派生类的描述中,()是错误的。 A.一个派生类可以作为另一个派生类的基类 B.派生类至少有一个基类 C.派生类的成员除了它自己的成员外,还包含了它的基类成员 D.派生类中继承的基类成员的访问权限到派生类保持不变 答案:D 分析:一个派生类可以作为另一个派生类的基类。无论是单继承还是多继承,派生类至少有 成 的成员时可能出现二义性。消除二义性的方法是采用作用域运算符。派生类和它的基类中出现同名函数时,不可能出现二义性。 例题4:多继承派生类构造函数构造对象时,()被最先调用。 A.派生类自己的构造函数 B.虚基类的构造函数 C.非虚基类的构造函数D.派生类中子对象类的构造函数 答案:B 分析:多继承派生类构造函数构造对象时,构造函数的调顺序是:虚基类的构造函数,派生类中子对象类的构造函数,派生类自己的构造函数。

例题5:C++类体系中,能被派生类继承的是()。 A.构造函数B.虚函数C.析构函数D.友元函数答案:B 分析:C++类体系中,构造函数、析构函数和友元函数是不能被派生类继承的. 例题6:设有基类定义: class Cbase { private: int a; protected: int b; public: int c; }; 用派生类中子对象类的析构函数,最后调用基类的析构函数。 例题11:设有以下类的定义: class A class B: protected A class C: private B { int A1; { int b1; { int c1; protected: int A2; protected: int b2; protected: int c2; public: int A3; public: int b3; public: int c3; }; }; }; 请按访问权限写出派生类C中具有的成员。 私有成员: (1)

继承与派生类

继承与派生类 知识要点 1.掌握继承和派生的定义,派生类的定义方法。 (1)掌握继承的两种类型:单继承和多继承。 (2)掌握private,public,protected三种继承方式的特点。继承方式决定了基类中的成员在派生类中的属性。三种继承方式的共同点:基类的private成员在派生类中不可见。区别:对于私有继承,基类的public、protected成员在派生类中作为private成员;对于公有继承,基类的public、protected成员在派生类中访问属性不变;对于保护继承,基类的public、protected成员在派生类中作为protected成员。 (3)掌握派生类中的构造函数和析构函数的使用。基类的构造函数和析构函数不能继承,所以必要时在派生类中定义自己的构造函数和析构函数。派生列的构造函数完成基类中新增数据成员和基类数据成员的初始化,基类数据成员的初始化通过基类构造函数来实现。 (4)掌握派生类的同名覆盖规则。 (5)掌握赋值兼容规则。基类对象可以使用公有派生类对象来代替,包括:派生类对象可以赋值给基类对象;派生类对象可以初始化基类对象的引用; 基类类型指针可以指向派生类对象。 2.掌握多重继承的概念、定义方法、多重继承派生类构造函数的执行顺序。 派生类构造函数的执行顺序是先执行所有基类的构造函数(顺序按照定义派生类时指定的各基类顺序),在执行对象成员所在类的构造函数(顺序按照他们在类中的声明顺序),最后执行派生类构造函数体中的内容。 3.掌握虚基类的概念和定义方法。在多重继承中,如果多条继承路径上有一个公共的基类,则在这些路径的汇合点上的派生类会产生来自不同路径的公共基类的多个拷贝,如果用virtual把公共基类定义成虚基类,则只会保留公共基类的一个拷贝。 2 典型题 1:下列对派生类的描述中,()是错误的。 A.一个派生类可以作为另一个派生类的基类 B.派生类至少有一个基类 C.派生类的成员除了它自己的成员外,还包含了它的基类成员 D.派生类中继承的基类成员的访问权限到派生类保持不变 2:派生类的对象对它的哪一类基类成员是可以访问的?() A.公有继承的基类的公有成员 B. 公有继承的基类的保护成员 C. 公有继承的基类的私有成员 D. 保护继承的基类的公有成员 3:关于多继承二义性的描述,()是错误的。 A.派生类的多个基类中存在同名成员时,派生类对这个成员访问可能出现二义性 B.一个派生类是从具有共同的间接基类的两个基类派生来的,派生类对该公共基类的访问可能出现二义性 C.解决二义性最常用的方法是作用域运算符对成员进行限定 D.派生类和它的基类中出现同名函数时,将可能出现二义性

实验四:派生类和继承(一)

福建农林大学金山学院实验报告 系(教研室):信息与机电工程系专业:计算机科学与技术年级: 实验课程:面向对象程序设计姓名:学号:  实验室号 计算机号 实验时间:指导教师签字:成绩: 实验4 派生类和继承(一) 一、实验目的和要求 (1)掌握派生类的声明与定义方法,进一步理解类的继承的概念,能够定义和使用类的继承关系。 (2)熟悉公有派生和私有派生的访问特性。 二、实验内容和原理 1、程序分析题(写出程序的输出结果,并分析结果)。

2、(1)定义一个基类animal,该类具有私有整型成员变量age,weight,构造派生类dog公有继承animal,dog类新增私有成员变量color,新增成员函数SetAge(int n)中直接给age赋值,新增成员函数SetWeight(int m)中直接给weight赋值,查看编译结果,并分析结果。(2)将类animal中的age和weight为公有成员,重做第一步,并分析结果。(3)将类animal中的age和weight为保护成员,重做第一步,并分析结果。(4)将派生类dog的继承方式改为私有继承方式和保护继承方式重做以上各小题,并分析结果。 三、实验环境 1. 硬件:PC机; 2. 软件:Windows操作系统、Visual C++ 6.0 四、算法描述及实验步骤 2.1 #include class animal {private:int age,weight;}; class dog:public animal

{private:char color[10]; public: int SetAge(int n) {age=n;return n;} int SetWeight (int m) {weight=m;return m; } }; int main() { int x,y; dog a; cout<<"请输入这条狗的岁数="; cin>>x;cout< class animal {public:int age,weight;}; class dog:public animal {private:char color[10]; public: int SetAge(int n) {age=n;return n;} int SetWeight (int m)

继承与派生练习题

习题八 第八章继承与派生 1.下列对派生类的描述中,()是错误的。 A.一个派生类可以作为另一个派生类的基类 B.派生类至少有一个基类 C.派生类的成员除了它自己的成员外,还包含了它的基类成员 D.派生类中继承的基类成员的访问权限到派生类保持不变 2. 派生类的对象对它的哪一类基类成员是可以访问的?() A.公有继承的基类的公有成员 B. 公有继承的基类的保护成员 C. 公有继承的基类的私有成员 D. 保护继承的基类的公有成员 3. 关于多继承二义性的描述,()是错误的。 A.派生类的多个基类中存在同名成员时,派生类对这个成员访问可能出现二义性B.一个派生类是从具有共同的间接基类的两个基类派生来的,派生类对该公共基类的访问可能出现二义性 C.解决二义性最常用的方法是作用域运算符对成员进行限定 D.派生类和它的基类中出现同名函数时,将可能出现二义性 4. 多继承派生类构造函数构造对象时,()被最先调用。 A.派生类自己的构造函数B.虚基类的构造函数 C.非虚基类的构造函数 D.派生类中子对象类的构造函数 5. C++类体系中,能被派生类继承的是()。 A.构造函数 B.虚函数 C.析构函数 D.友元函数 6. 设有基类定义: class Cbase { private: int a; protected: int b; public: int c; }; 派生类采用何种继承方式可以使成员变量b成为自己的私有成员( ) A. 私有继承 B.保护继承 C. 公有继承 D.私有、保护、公有均可 7. 指出下列对定义重载函数的要求中,哪些是错误的提法。 A.要求参数的个数不同。 B.要求参数中至少有一个类型不同。 C.不要求函数的返回值不同。 D. 要求参数的个数相同时,参数类型不同。 8. 下面关于友元的描述中,错误的是()。 A.友元函数可以访问该类的私有数据成员 B.一个类的友元类中的成员函数都是这个类的友元函数 C.友元可以提高程序的运行效率 D.类与类之间的友元关系可以继承 9. 下述静态成员的特性中,()是错误的。 A.静态成员函数不能利用this指针 B.静态数据成员要在类体外进行初始化

实验三 派生类与继承

实验三派生类与继承 一、实验目的 1、学习类的继承,能够定义和使用类的继承关系。 2、学习派生类的声明与定义方法。 3、掌握类的定义和对象的声明。 4、熟悉公有派生和私有派生的访问特性。 5、掌握派生类构造函数和析构函数的执行顺序。 6、掌握利用访问声明调整基类成员在派生类中的访问属性。 二、试验内容 1、下面的程序可以输出ASCII字符与所对应的数字的对照表。修改下列程序,使其可以输出字母a到z(或任意两个字符间)与所对应的数字的对照表。class table { public: table(int p) { i=p; } void ascii(void); protected: int i; }; void table::ascii(void) { int k=1; for (;i<127;i++) { cout<

{ c=m; } void print(void); protected: char *c; }; void der_table::print(void) { cout< class table { public: table(int p) { i=p; } void ascii(void); protected: int i; }; void table::ascii(void) { int k=1; for (;i<123;i++) { cout<

实验四 继承与派生(学生)

实验四:继承与派生(2) 一、实验目的和要求 (1)理解单继承与多继承,掌握多继承派生类的声明方式。 (2)掌握多继承派生类构造函数的定义的一般形式。 (3)掌握多继承构造函数与析构函数的执行顺序。 (4)掌握含有对象成员的派生类构构造函数的特点。 (5)理解类的继承可能发生的“二义性”问题(同名成员的访问) 二、知识点回顾 多继承构造函数定义的一般形式如下: 派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),…,基类名n(参数表n) { // 派生类新增成员的初始化语句 } class B: public A class D:public A, public B, public C { { B(int i,int j):A( i) { ….. D(int i,int j,int k, int l):A( i),B(j ),C( k),ob(l ) } { ……. } }; }; ……. D obj(1,2,3,4); 多重继承构造函数的执行顺序与单继承下的构造函数执行顺序相同: (1)先执行基类的构造函数。 (2)再执行对象成员的构造函数。 (3)最后执行派生类的构造函数体。 说明:处于同一层的各个基类的构造函数的执行顺序, 取决于声明派生类时所指定的各个基类的顺序, 与派生类构造函数中所定义的成员初始化列表中的顺序并没有关系. 说明:派生类构造函数后如未列出基类构造函数的调用,默认调用的是无参或带默认参数的构造函数。 三、实验内容 1. P191题4.15输入下列程序,分析程序运行结果........ 。 理解多重继承(画出类的层次结构图),注意派生类构造函数的定义格式,注意构造函

继承与派生习题

1.概念填空题 1.1在C++中,三种派生方式的说明符号为、、不加说明,则默认的派生方式为。 1.2当公有派生时,基类的公有成员成为派生类的;保护成员成为派生类的;私有成员成为派生类的。当保护派生时,基类的公有成员成为派生类的;保护成员成为派生类的;私有成员成为派生类的。 1.3 派生类的构造函数一般有3项工作要完成:首先,其次,最后。 1.4多继承时,多个基类中的同名的成员在派生类中由于标识符不唯一而出现。在派生类中采用或来消除该问题。 2.选择题 2.1下面对派生类的描述中,错误的是()。 A.一个派生类可以作为另外一个派生类的基类 B.派生类至少有一个基类 C.派生类的成员除了它自己的成员外,还包含了它的基类的成员 D.派生类中继承的基类成员的访问权限到派生类中保持不变 2.2下列对友元关系叙述正确的是()。 A.不能继承 B.是类与类的关系 C.是一个类的成员函数与另一个类的关系 D.提高程序的运行效率 2.3当保护继承时,基类的()在派生类中成为保护成员,不能通过派生类的对象来直接访问。 A.任何成员B.公有成员和保护成员 C.公有成员和私有成员D.私有成员 2.4在公有派生情况下,有关派生类对象和基类对象的关系,不正确的叙述是()。 A.派生类的对象可以赋给基类的对象 B.派生类的对象可以初始化基类的引用 C.派生类的对象可以直接访问基类中的成员 D.派生类的对象的地址可以赋给指向基类的指针 2.5有如下类定义: class MyBASE{ int k; public: void set(int n) {k=n;} int get( ) const {return k;} }; class MyDERIVED: protected MyBASE{ protected; int j; public: void set(int m,int n){MyBASE::set(m);j=n;} int get( ) const{return MyBASE::get( )+j;}

C继承与派生类习题

第九章继承与派生类 9. 2 典型例题分析与解答 例题1:下列对派生类的描述中,()是错误的。 A—个派生类可以作为另一个派生类的基类 B.派生类至少有一个基类 C.派生类的成员除了它自己的成员外,还包含了它的基类成员 D.派生类中继承的基类成员的访问权限到派生类保持不变 答案:D 分析:一个派生类可以作为另一个派生类的基类。无论是单继承还是多继承,派生类至少有一个基类。派生类的成员除了它自己的成员外,还包含了它的基类成员。派生类中继承的基类成员的访问权限到派生类受继承方式影响的,对于私有继承,基类的 public ,protected 成员在派生类中作为private 成员;对于公有继承,基类的public ,protected 成员在派生类中访问属性不变;对于保护继承,基类的 public 、protected 成员在派生类中作为protected 成员。 例题2:派生类的对象对它的哪一类基类成员是可以访问的() A.公有继承的基类的公有成员 B.公有继承的基类的保护成员 C. 公有继承的基类的私有成员 D. 保护继承的基类的公有成员 答案: A 分析:公有继承的基类的公有成员在派生类中保持公有访问权限,所以派生类对象可以访问它;公有继承的基类的保护成员在派生类中保持保护访问权限,所以派生类对象不

可以访问它;基类的私有成员不能被派生到派生类中,所以派生类

对象不可以访问它;保护继承的基类的公有成员在派生类中变成保护的访问权限,所以派生类对象不可以访问它。 例题3:关于多继承二义性的描述,()是错误的。 A.派生类的多个基类中存在同名成员时,派生类对这个成员访问可能出现二 义性 B.—个派生类是从具有共同的间接基类的两个基类派生来的,派生类对该公 共基类的访问可能出现二义性 C.解决二义性最常用的方法是作用域运算符对成员进行限定 D.派生类和它的基类中出现同名函数时,将可能出现二义性 答案:D 分析:出现二义性有两种情况:调用不同基类的相同成员时可能出现二义性;访 问共同基类的成员时可能出现二义性。消除二义性的方法是采用作用域运算符。 派生类和它的基类中出现同名函数时,不可能出现二义性。 例题4:多继承派生类构造函数构造对象时,()被最先调用。 A.派生类自己的构造函数 B.虚基类的构造函数 C.非虚基类的构造函数 D.派生类中子对象类的构造函数 答案:E 分析:多继承派生类构造函数构造对象时,构造函数的调顺序是:虚基类的构造函数,派生类中子对象类的构造函数, 派生类自己的构造函数。 例题5: C++类体系中,能被派生类继承的是()。 A.构造函数 B.虚函数 C.析构函数 D.友元函数 答案:E 分析:C++类体系中,构造函数、析构函数和友元函数是不能被派生类继承的. 例题6:设有

实验八 派生类与继承

湖南文理学院实验报告 课程名称:C语言实验实验名称:派生类与继承成绩: 学生姓名:易德宗专业:计科班级、学号201417010105计一同组者姓名: 实验日期:2015-5-12 一、实验目的 1)学习类的的继承,能够定义和使用类的继承关系; 2)学习派生类的声明与定义方法; 3)熟悉公有派生和私有派生的访问特性。 二、实验内容及要求 1、输入下列程序。 #include class Base { public: void setx( int i) { x=i; } int getx( ) {return x;} int x; }; class Derived:public Base { public: void sety( int i) { y=i; } int gety( ) {return y;} voif show( ) {cout<<”Base::x=”<

Derived::y=25 Press any key to continue (2)按下列要求,对程序进行修改后再调试,指出调试中出错的原因。 ①将基类Base 中的数据成员x的访问权限改为private时,会出现哪些错误?为什么?cannot access private member declared in class 'Base' 不能通过cout<<"Base::x="<

派生类与继承

派生类与继承(一)(2课时) 实验目的和要求: 1.理解类的继承的概念,能够定义和使用类的继承关系。 2.掌握派生类的声明与定义方法。 3.理解不同的继承类型 预习内容: 派生类的声明与定义;不同的继承类型的差异。 实验内容: 1. 下面是一个含有派生类的程序: #include class A { // protected: private: int m,n; public: void set(int a, int b) { m=a; n=b; } void show() { cout << m << " " << n <

class MyArray { public: MyArray(int leng); ~ MyArray(); void Input(); void Display(); Protected: int *alist; //指向动态申请的一组空间 int length; //整数的个数 }; 要求: (1)基类中有构造函数、析构函数、输入数据和输出数据的函数。 (2)定义一个SortArray继承自MyArray,在该类中定义函数实现排序功能。 (3)定义一个类ReArray继承自MyArray,在该类中定义函数实现逆转功能。 (4)定义一个类AverArray继承自MyArray,在该类中定义函数Aver()求解整数的平 均值。 3. 建立一个建筑物类的层次体系。其中,基类building包括保护数据成员name(建 筑物名称)、floors(层数)和areas(总面积),由building类派生住宅类house 和办公楼类office;住宅类house包括私有数据成员rooms(房间数)和balcony(阳台数);办公楼类office包括私有数据成员offices(办公室数)和meetingrooms (会议室数)。住宅类house和办公楼类office都含有构造函数和print函数,以分别用于对数据成员的初始化和输出。在主函数中,实例化house类和office类对象并且将其数据输出。 实验五:派生类与继承(二) (2课时) 实验目的和要求: 1.熟悉公有派生和私有派生的访问特性。 2. 掌握单继承与多继承 3.学习虚基类在解决二义性问题中的作用。 预习内容: 公有派生和私有派生的访问特性 实验内容: 1、定义NewArray类,同时继承了上次实验SortArray、ReArray和AverArray,使得 NewArray类的对象同时具有排序、逆转和求平均值的功能。在继承的过程中声明为虚

继承与派生类知识要点

继承与派生类 知识要点 1.1.掌握继承和派生的定义,派生类的定义方法。 (1)掌握继承的两种类型:单继承和多继承。 (2)掌握private,public,protected三种继承方式的特点。继承方式决定了基类中的成员在派生类中的属性。三种继承方式的共同点:基类的private成员在派生类中不可见。 区别:对于私有继承,基类的public、protected成员在派生类中作为private成员;对于公有继承,基类的public、protected成员在派生类中访问属性不变;对于保护继承,基类的public、protected成员在派生类中作为protected成员。 (3)掌握派生类中的构造函数和析构函数的使用。基类的构造函数和析构函数不能继承,所以必要时在派生类中定义自己的构造函数和析构函数。派生列的构造函数完成基类中新增数据成员和基类数据成员的初始化,基类数据成员的初始化通过基类构造函数来实现。 (4)掌握派生类的同名覆盖规则。 (5)掌握赋值兼容规则。基类对象可以使用公有派生类对象来代替,包括:派生类对象可以赋值给基类对象;派生类对象可以初始化基类对象的引用;基类类型指针可以指向派生类对象。 2.2.掌握多重继承的概念、定义方法、多重继承派生类构造函数的执行顺序。派生类构造函数的执行顺序是先执行所有基类的构造函数(顺序按照定义派生类时指定 的各基类顺序),在执行对象成员所在类的构造函数(顺序按照他们在类中的声明 顺序),最后执行派生类构造函数体中的内容。 3.3.掌握虚基类的概念和定义方法。在多重继承中,如果多条继承路径上有一个公共的基类,则在这些路径的汇合点上的派生类会产生来自不同路径的公共基类的多 个拷贝,如果用virtual把公共基类定义成虚基类,则只会保留公共基类的一个拷贝。

第4章 继承与派生习题 参考答案

第4章继承与派生习题参考答案 一、选择题(共30分,每题1分) 二、填空题(共17分,每空1分) 1. 基类 2. (1)派生类(2)派生类中子对象类(3)基类 3. (1)c1、b2、b3、A2、A3 (2)c2 (3)c3 4. (1)基类 (2)派生类 5. ~A() {cout<<"bb";} 6. A(aa),c(aa+1) 或c(aa+1),A(aa) 7. (1)公有成员(2)公有和保护成员 8. (1)保护(2)成员函数(3)不可访问 三、改错题(共6分,每题2分) 1. 保护继承方式使基类的public成员在派生类中的访问属性变为protected,所以派生类Rectangle的对象r不能直接访问基类的成员函数move()、getx()和gety()。其改正方法有两种: 1)将Rectangle的继承方式改为公有继承class Rectangle: public Point; 2)在Rectangle类中重定义move(),getx()和gety()函数,覆盖基类的同名函数。 void Rectangle::move(intxoffset,intyoffset){Point::move(xoffset,yoffset);} void Rectangle::getx(){return Point::getx();} void Rectangle::gety(){return Point::gety();} 2. 分析:类A、B中有同名公有数据成员x和同名成员函数display(),在主函数中访问对象myc的数据成员x是无法确定是访问从A中继承的还是从B中继承的x;调用成员函数也是如此,无法确认是调用类A中的还是类B中的,产生二义性。 改正方法,可以用作用域区分符加以限定,如改成: myc.A::x=10; myc.A::display(); 或myc.B::x=10; myc.B::display(); 3. #include using namespace std; class Member{

面向对象派生类与继承实验三

福建农林大学计算机与信息学院实验报告 系:计算机专业:计算机科学与技术年级:2009级 姓名:学号:实验室号田507、513、514 计算机号 实验时间:2011年10月25日、11月1日指导教师签字:成绩: 实验3派生类与继承 一、实验目的和要求 (1)掌握派生类的声明与定义方法,进一步理解类的继承的概念,能够定义和使用类的继承关系。 (2)熟悉公有派生和私有派生的访问特性。 (3)了解虚基类在解决二义性问题中的作用。 二、实验内容和原理 (1)定义一个基类Animal,有私有整型成员变量age,构造其派生类dog,在其成员函数SetAge(int n)中直接给age赋值,看看会有什么问题,把age改为公有成员变量,还会有问题吗?编程试试看。 (2)定义一个基类BaseClass,有整型成员变量Number ,构造其派生类DerivedClass,观察构造函数和析构函数的执行情况。 (3)定义一个车(vehicle)基类,具有MaxSpeed、Weight等成员变量,Run、Stop 等成员函数,由此派生出自行车(bicycle)类、汽车(motorcar)类。自行车类有高度(height)等属性,汽车类有座位数(SeatNum)等属性。从bicycle和motorcar派生出摩托车(motorcycle)类,在继承过程中,注意把vehicle设置为虚基类。如果不把vehicle设置为虚基类,会有什么问题?编程试试看。 (4)设计一个用于人事管理的People(人员)类。考虑到通用性,这里只抽象出所有类型人员都具有的属性:number(编号)、sex(性别)、id(身份证号)等等。用成员函数实现对人员信息的录入和显示。要求包括:构造函数和析构函数、拷贝构造函数、内联成员函数。 从people(人员)类派生出student(学生)类,添加属性:班号char classNo[7];从people类派生出teacher(教师)类,添加属性:职务char principalship[11]、部门char department[21]。从student类派生出graduate(研究生)类,添加属性:专业char subject[21]、导师teacher adviser;从graduate类和teacher类派出TA(助教生)类,注意虚基类的使用。重载相应的成员函数,测试这些类。 三、实验环境 联想计算机,Windows XP操作系统,Visual C++ 6.0 四、算法描述及实验步骤 (1)编写源程序。 (2)检查程序有无错误(包括语法错误和逻辑错误),有则改之。 (3)编译和连接,仔细分析编译信息,如有错误应找出原因并改正之。 (4)运行程序,分析结果。 (5)将调试好的程序保存在自己的用户目录中,文件名自定。 五、调试过程 1.

第4章 继与派生

第4章继承与派生 例4-1 示例公有继承。 //example41.cpp #include #include class Person //定义基类Person { public: //外部接口Person(const char* Name,int Age,char Sex); //基类构造函数 char* GetName() { return(name); } int GetAge(); //基类成员函数的声明char GetSex(); void Display(); private: char name[11]; char sex; protected: //保护成员int age; }; Person::Person(const char *Name,int Age,char Sex) //基类构造函数的实现{ strcpy(name,Name); age=Age; sex=Sex; } int Person::GetAge() //基类成员函数的实现{ return(age); } char Person::GetSex() { return(sex); } void Person::Display() { cout<<"name:"<

{ public: //外部接口 Student(char* pName,int Age,char Sex,char* pId,float Score): Person(pName,Age,Sex) //调用基类的构造函数初始化基类的数据成员{ strcpy(id,pId); //学生类的数据初始化 score=Score; } char* GetId(char* pId) //派生类的新成员 { return(id); } float GetScore() //派生类的新成员 { return score; } void Display(); //派生类的新成员 private: char id[9]; float score; }; void Student::Display() //派生类的成员函数的实现 { cout<<"id:"<>name; Person p1(name,29,′m′); //基类对象 p1.Display(); //基类对象访问基类公有成员函数 char pId[9]; cout<<"Enter a student′s name:"; cin>>name; Student s1(name,19,′f′,"03410101",95); //派生类对象 cout<<"name:"<

【习题】继承与派生

继承与派生 【实验目的】: 1.掌握派生类的使用方法。 2.掌握派生类的构造函数和析构函数。 【实验内容】: 1.定义一个哺乳动物Mammal类,再由此派生出狗Dog类,定义一个Dog类的对象, 观察基类与派生类的构造函数与析构函数的调用顺序。 2.编写一个求出租车收费的程序,输入起始站、终止站和路程,计费公式是起价8 元,其中含3公里费用,以后每半公里收费0.7元。

生类Student和教师类Teacher,并用一些数据进行测试。 4.定义一个rectangle类,它包含两个数据成员length和width;以及包含用于求长方 形面积的成员函数。再定义rectangle的派生类cuboid,它包含一个新数据成员height和用来求长方体体积的成员函数。在主函数中,使用2个类,求某个长方形 的面积和某个长方体的体积。

5.声明一个Object类,有数据成员weight和相应的操作函数,由此派生出的Box类, 增加数据成员height和weight及相应的操作函数,声明一个Box对象,观察构造 函数和析构函数的调用顺序。 生出圆类Circle和三角形类Triangle,并求各自的面积。

7.设计一个建筑物基类Buinding,由它派生出宿舍类Hostel和教学楼Classroom,前 者包括楼名、总层数、住户数、总住人数和总面积,后者包括楼名、总层数、教 室数、灭火器数和总面积。

8.派生类构造函数执行的次序是怎样的? 先构造父类,再构造派生类,析构时先析构派生类,再析构父类。 9.比较类的三种继承方式public公有继承、protected保护继承、private私有继承之 间的差别。 公有继承:派生类函数可调用父类公有成员,父类在派生类中公有私有保护性质不变。 保护继承:派生类可调用父类原先的公有和保护成员,父类在派生类中公有和保护成员变为保护成员,私有成员仍为私有。 私有继承:派生类可调用父类原先的公有成员,父类在派生类中公有保护私有全部变成私有,再下一层的派生类无法访问它的任何成员。 10.如果在派生类B已经重载了基类A的一个成员函数fn1(),没有重载成员函数fn2(), 如何调用基类的成员函数fn1()、fn2()? 因为在派生类B已经重载了基类A的一个成员函数fn1(),所以要用作用域运算符对fn1()函数加以限定,调用基类的成员函数fn1()是A::fn1();因为在派生类B没有重载成员函数fn2(),所以直接可调用fn2()。 11.(选做题)设计一个圆类Circle和一个桌子类Table,另设计一个圆桌类RoundTable, 它是从前两个类派生出来的,要求输出一个圆桌的高度、桌面半径、面积和颜色等数据。 提示:利用多继承class RoundTable:public Table,public Circle

C++习题第07章 继承和派生

第07章继承和派生 (1)面向对象程序设计的____机制实现了代码重用,有效地缩短了程序的开发周期。(2)在继承关系中,被继承的类称为____,通过继承关系定义出来的新类称为_____。(3)派生类的继承方式有____ ,____ 和____3种,其中默认的继承方式为____.。 (4)公有继承时,基类的私有成员为跑生类的____成员,基类的保护成员成为派生类的____成员,基类的公有成员成为派生类的____成员。 (5)在派生类中不能访问基类的____成员,这符合面向对象程序设计的封装思想。(6)C++语言不仅支持____继承,也支持____继承。 (7)可以把派生类的对象当做基类对象来处理,这是____的概念。 (8)根据联编实现的不同极端,可将其分为____联编和____联编两种。 (9)声明了纯虚函数的类,称为____。 2.选择题 (1)下列关于派生类的描述中,不正确的是() A)派生类的成员除了包含它自己的成员的访问权限在派生类中保持不变 B)派生类中继承的基类 C)派生类至少有个一个基类 D)一个派生类可以作为另一个派生的基类 (2)在保护继承中,基类的私有成员变成派生类的() A)不可访问成员B)稀有成员C)保护成员D)公有成员 (3)若有如下类声明: Class Base { int k; Public: Void set(int n){k=n;} Int get()const{return k;} }; Class Derived:protected Base {protected: Int j; Public: Void set(int m,int n) { Base::set(m); j=n; } Int get()const { return Base::get()+j; } }; 则类Derived中保护的数据成员和成员函数的个数是() A)4 B)3 C)2 D)1 (4)在派生类的结构函数的初始化表中,不能包含()

相关文档
最新文档