实验六 继承与派生

实验六 继承与派生

一、实验目的

1.掌握派生类的定义方法和实现,能正确访问继承层次中的各种类成员。

2.掌握继承机制中派生类的构造函数和析构函数的执行顺序。

二、实验内容

1.请改写教材例题7-1的Rectangle类公有继承Point类。要求:(30分)

(1)为派生类增加构造函数、析构函数,及其它必要的函数。

(2)派生类中有能够打印矩形信息的函数,包括的信息有矩形点的位置坐标、矩形长宽值及面积。

2.设计一个基类:学生类(Student),采用公有继承的方式派生出一个研究生类(PostGraduate),要求:(20分)

(1)Student类中包含:学号、姓名、性别、专业。

(2)要求在PostGraduate类中增加导师(tutor)、津贴(allowance)、研究方向(researchArea)。

(3)两个类中都包含:display()函数,用于输出本类中的成员信息。

可以用如下代码测试所建立的类是否正确。

int main(){
    Student s1(“20190001”, “Michael”, “Male”, “Computer Science”);
    //参数分别为:学号,姓名,性别,专业
    s1.display();

    PostGraduate p1(……);//导师:“Liu”,津贴:“1000”,研究方向:“Deep learning”

    p1.display();
        return 0;
}

3.定义一个基类BaseClass,它有公有成员函数func1(),func2(),函数内打印出类名及函数名,有私有数据成员int i。从它派生出类DerivedClass,有公有成员函数func1(),函数内打印出类名及函数名,私有数据成员int j,在主函数中分别用BaseClass及Derivedclass类的指针去调用func1(),func2,观察运行结果。要求:(20分)

(1)注意派生类的构造函数须写正确;

(2)分析通过指针调用成员函数的运行结果。

4.定义一个Document类,有数据成员name,从Document派生出Book类,增加数据成员pageCount。观察基类与派生类的构造函数和析构函数的调用顺序。(20分)

5.组合与继承有什么共同点和差异?通过组合生成的类与被组合的类之间的逻辑关系是什么?继承呢?请分析总结。(5分)

三、实验步骤及结果

题目1:

解题思路:

1.派生类构造函数注意要给基类的构造函数传参数

2.面积通过函数调用私有数据成员进行计算最后return回结果即可,之后直接通过对象调用公有函数直接输出面积

3.派生类通过公有函数去访问基类私有数据成员来输出矩形的信息

程序代码:

//Point.h
#ifndef _POINT_H
#define _POINT_H
class Point//基类Point类的定义
{
    public://公有函数成员 
    	Point(){}//默认构造函数 
    	Point(float xx,float yy):x(xx),y(yy){}//构造函数 
    	~Point(){} //析构函数 
        void initPoint(float x = 0, float y = 0)
		{ 
            this->x = x; 
            this->y = y;
        }
        void move(float offX, float offY)
		{ 
            x += offX; 
            y += offY;
        }
        float getX() const { return x; }
        float getY() const { return y; }
    private://私有数据成员  
        float x, y;
};
#endif //_POINT_H
//Rectangle.h
#ifndef _RECTANGLE_H
#define _RECTANGLE_H
#include "Point.h"
class Rectangle: public Point//派生类定义部分
{ 
public://新增公有函数成员
	Rectangle(){}//默认构造函数 
	Rectangle(float xx, float yy, float ww, float hh):Point(xx,yy),w(ww),h(hh){}//构造函数 
	~Rectangle(){}//析构函数 
    void initRectangle(float x, float y, float w, float h)
	 {
        initPoint(x, y);//调用基类公有成员函数
        this->w = w;
        this->h = h;
    }
    void printInfo()//打印矩形信息(矩形点的位置坐标、矩形长宽值及面积。) 
	{
		cout << "The data of rect1(x,y,w,h): " << endl;
		cout << "Point:" << "(" << getX() <<","<< getY() << ")" <<endl;
		cout << "Width:" << getW() << endl;
		cout << "Height:" << getH() << endl;
		cout << "Area:" << getArea() << endl;
	}
	float getArea(){return getH()*getW();}//计算面积 
    float getH() const { return h; }
    float getW() const { return w; }
private://新增私有数据成员
    float w, h;
};
#endif //_RECTANGLE_H
/*
程序名:题目1.cpp
功能:请改写教材例题7-1的Rectangle类公有继承Point类。要求:(30分) 
(1)为派生类增加构造函数、析构函数,及其它必要的函数。 
(2)派生类中有能够打印矩形信息的函数,包括的信息有矩形点的位置坐标、矩形长宽值及面积。 
日期:2021.11.26
版本:1.0
*/
//main.cpp
#include <iostream>
#include <cmath>
using namespace std;
#include "Rectangle.h"
int main() 
{
    Rectangle rect;//定义Rectangle类的对象
    rect.initRectangle(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;
        
        
    Rectangle rect1(3,4,21,11);//用构造函数创建对象
    rect1.move(2,2);//移动矩形位置 
    rect1.printInfo();//打印矩形信息 
    
    return 0;
}

运行结果截图:
图片描述
图片描述

题目2:

解题思路:

1.派生类构造函数需要给基类构造函数传参数

2.在类外要用公有函数去调用基类的私有数据成员

3.如要通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符(::)来限定。

程序代码:

/*
程序名:题目2.cpp
功能:设计一个基类:学生类(Student),采用公有继承的方式派生出一个研究生类(PostGraduate),要求:(20分)
(1)Student类中包含:学号、姓名、性别、专业。
(2)要求在PostGraduate类中增加导师(tutor)、津贴(allowance)、研究方向(researchArea)。 
(3)两个类中都包含:display()函数,用于输出本类中的成员信息。
日期:2021.11.26
版本:1.0
*/
#include <iostream>
#include <string>
using namespace std;
class Student//Student类的定义 
{
private://私有数据成员 
	string id;
	string name;
	string gender;
	string major;
public://外部接口
	Student(string ID, string NAME, string GENDER, string MAJOR);//构造函数
	~Student();//析构函数 
	virtual void display()const; //输出Student类中的成员信息 
};
Student::Student(string ID, string NAME, string GENDER, string MAJOR) :id(ID), name(NAME), gender(GENDER), major(MAJOR) {}//构造函数实现 
Student::~Student() {}//析构函数实现 
void Student::display() const//输出Student类中的成员信息 
{
	cout << "Id:" << id << endl;
	cout << "Name:" << name << endl;
	cout << "Gender:" << gender << endl;
	cout << "Major:" << major << endl;
}
class PostGraduate :public Student//公有派生类PostGraduate定义 
{
private://私有数据成员
	string tutor;
	string allowance;
	string researchArea;
public://外部接口
	PostGraduate(string ID, string NAME, string GENDER, string MAJOR, string TUTOR, string ALLOWANCE, string RESEARCHAREA);//构造函数
	~PostGraduate();//析构函数 
	virtual void display()const; //输出PostGraduate类中的成员信息 
};
void PostGraduate::display() const//输出PostGraduate类中的成员信息 
{
	Student::display();//加作用域操作符(::)来限定。
	cout << "Tutor:" << tutor << endl;
	cout << "Allowance:" << allowance << endl;
	cout << "ResearchArea:" << researchArea << endl;
}
PostGraduate::PostGraduate(string ID, string NAME, string GENDER, string MAJOR, string TUTOR, string ALLOWANCE, string RESEARCHAREA) :
	Student(ID, NAME, GENDER, MAJOR), tutor(TUTOR), allowance(ALLOWANCE), researchArea(RESEARCHAREA) {}//构造函数实现 
PostGraduate::~PostGraduate() {}//析构函数实现
int main() {
	Student s1("20190001", "Michael", "Male", "Computer Science");//参数分别为:学号,姓名,性别,专业
	s1.display();
	cout << "***********************************************************" << endl;
	PostGraduate p1("20210001", "Jane", "Female", "Software Engineering", "liu", "1000", "Deep learning");//导师:“Liu”,津贴:“1000”,研究方向:“Deep learning”
	p1.display();
	system("pause");
	return 0;
}

运行结果截图:
图片描述

题目3:

解题思路:

1.若未特别限定,则通过派生类对象使用的是派生类中的同名成员。

2.派生类构造函数需要给基类构造函数传参数

程序代码:

/*
程序名:题目3.cpp
功能:定义一个基类BaseClass,它有公有成员函数func1(),func2(),函数内打印出类名及函数名,有私有数据成员int i。
从它派生出类DerivedClass,有公有成员函数func1(),函数内打印出类名及函数名,私有数据成员int j
在主函数中分别用BaseClass及Derivedclass类的指针去调用func1(),func2,观察运行结果。要求:(20分)
(1)注意派生类的构造函数须写正确;
(2)分析通过指针调用成员函数的运行结果。
日期:2021.11.27
版本:1.0
*/
#include <iostream>
using namespace std;
class BaseClass//BaseClass类定义 
{
private://私有数据成员
	int i;
public://外部接口
	BaseClass(int ii);//构造函数 
	~BaseClass();//析构函数 
	void func1(); 
	void func2(); 
};
BaseClass::BaseClass(int ii){};//构造函数实现
BaseClass::~BaseClass(){}//析构函数实现 
void BaseClass::func1()
{	
	cout << "BaseClass:func1()" << endl;
}
void BaseClass::func2()
{
	cout << "BaseClass:func2()" << endl;
}
class DerivedClass:public BaseClass//公有继承派生类DerivedClass定义 
{
private://私有数据成员 
	int j;
public://外部接口 
	DerivedClass(int ii,int jj);//构造函数
	~DerivedClass();//析构函数 
	void func1();
};
DerivedClass::DerivedClass(int ii,int jj):BaseClass(ii),j(jj){}//构造函数实现
DerivedClass::~DerivedClass(){}//析构函数实现
void DerivedClass::func1()
{
	cout << "DerivedClass:func1()" << endl;
}

int main()
{	
	//BaseClass类对象指针定义 
	BaseClass p1(1);
	BaseClass *ptr1;
	ptr1=&p1;
	//用BaseClass类对象指针去调用func1,func2 
	ptr1->func1();
	ptr1->func2();
	cout << "************************" << endl;
	//DerivedClass类对象指针定义 
	DerivedClass p2(1,2);
	DerivedClass *ptr2;
	ptr2=&p2;
	//用DerivedClass类对象指针去调用func1,func2 
	ptr2->func1();
	ptr2->func2();
	system("pause");
	return 0;
}

运行结果截图:
图片描述

分析:

1.由于同名隐藏规则,派生类指针调用func1访问的是派生类的func1

2.由于派生类中没有func2,是从基类中继承过来的,在派生类中没有定义同名成员,因此派生类指针调用func2访问的是基类的func2

题目4:

解题思路:

构造函数的执行顺序:

1.调用基类构造函数。【顺序按照它们被继承时声明的顺序(从左向右)。】

2.对初始化列表中的成员进行初始化。

(1)顺序按照它们在类中定义的顺序。

(2)对象成员初始化时自动调用其所属类的构造函数。由初始化列表提供参数。

3.执行派生类的构造函数体中的内容。

程序代码:

/*
程序名:题目4.cpp
功能:定义一个Document类,有数据成员name,从Document派生出Book类,增加数据成员pageCount。 
观察基类与派生类的构造函数和析构函数的调用顺序。(20分) 
日期:2021.11.27
版本:1.0
*/
#include <iostream>
#include <iostream>
using namespace std;
class Document//Document类的定义 
{
private://私有数据成员
	string name; 
public://外部接口
	Document(string Name);//构造函数
	~Document();//析构函数 
};
Document::Document(string Name):name(Name)//构造函数实现 
{
	cout << "Calling the constructor of Document. " << name << endl;
}
Document::~Document()//析构函数实现 
{
	cout << "Calling the destructor of Document. " <<  name << endl;	
}
class Book:public Document//公有继承派生类Book类的定义 
{
private://私有数据成员
	int pageCount;
	Document d1;
public://外部接口
	Book(string Name1,int PageCount,string Name2);//构造函数
	~Book();//析构函数 
};
Book::Book(string Name1,int PageCount,string Name2):Document(Name1),pageCount(PageCount),d1(Name2)//构造函数实现 
{
	cout << "Calling the constructor of Book. " << pageCount << endl;
}
Book::~Book()
{
	cout << "Calling the destructor of Book. " << pageCount << endl;
}

int main()
{
	Book("ZhangSan",666,"LiSi");//基类ZhangSan,派生类666,内嵌基类对象LiSi 
	system("pause");
	return 0;
}

运行结果截图:
图片描述

分析:

构造函数:

1.先调用基类构造函数输出ZhangSan

2.再调用基类构造函数为内嵌对象初始化输出LiSi

3.最后调用派生类构造函数输出666

析构函数:与构造函数正好相反

题目5:

问题: 组合与继承有什么共同点和差异?通过组合生成的类与被组合的类之间的逻辑关系是什么?继承呢?请分析总结。(5分)

回答:

(1)共同点:继承和组合的使用都可以减少重复代码,同时若类B是类A的内嵌对象,则B类具有A类的全部数据。

不同点:组合是通过在其他类中定义对象来使用类中的方法和属性,不能访问父类的任何接口。而继承则是从父类中得到方法和属性,即可以得到父类的全部接口,并加以调用。

(2)通过组合生成的类和被组合的类之间的逻辑关系:组合生成的类具有被组合类的全部内容,但是并不包括被组合类的全部接口,就是通过被组合的类的对象只能访问组合类的成员函数,但不能直接访问到被组合类的成员函数。

(3)继承类与基类的逻辑关系:即通过派生类的对象不但可以访问派生类的成员函数,也能访问基类的成员函数,派生类是可以完全继承基类的任何内容的包括全部接口。

四、实验小结

问题与解决办法:

(1)问题:派生类构造函数报错

解决方法:派生类构造函数需要给内嵌对象以及基类构造函数传递参数。

(2)问题:公有继承派生类成员函数访问基类私有数据成员报错

解决方法:派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;需要通过访问基类的公有函数去访问私有数据成员

(3)问题:派生类对象无法访问基类中被隐藏的同名成员

解决方法:通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符(::)来限定。

心得体会:

1.本章内容感觉学起来轻松,因为重点知识比较明确,例如三种继承方式的不同,构造函数和析构函数的调用顺序,派生类成员的标识与访问等等,只要掌握了相关重点,本章题目写起来也比较轻松应手。

2.有时候自己找不到自己的错误,即使这个错误是easy水平自己看了都要骂自己的题,但是有时候就是找不到,这时候可以通过找同学帮忙看一下自己哪里错了,旁观者清有时候其他人一眼就看出你哪里犯了白痴错误。另外单步调试也是一个非常好用的方法,利用单步调试可以很好的帮助寻找错误区间。

3.课本上的题目真的要自己去单步调试一步一步运行,这样才能很好地去理解重点知识,例如构造函数调用顺序等,这样可以更好的加深自己对知识的理解,再配合上写题目就可以理解本章大部分内容,实在不行就去问老师。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是一只大狸子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值