任务三 类的继承与派生

第1关:公有继承 —— 学生信息类

相关知识

继承

继承是使代码可以复用的重要手段,也是面向对象程序设计的核心思想之一。简单的说,继承是指一个对象直接使用另一对象的属性和方法。

C++ 中的继承关系就好比现实生活中的父子关系,继承一笔财产比白手起家要容易得多,原始类称为基类,继承类称为派生类,基类是对派生类的抽象,派生类是对基类的具体化。它们是类似于父亲和儿子的关系,所以也分别叫父类和子类。而子类又可以当成父类,被另外的类继承。

继承方式

不同的继承方式决定了基类成员在派生类中的访问属性,主要体现在:

派生类成员对基类成员的访问权限;

通过派生类对象对基类成员的访问权限。

对于派生类的成员或者派生类对象访问自己类的成员不讨论,跟一般类一样,下面只讨论对基类的成员的访问。

公有继承:基类的 public 和 protected 成员访问属性在派生类中保持不变;基类的 private 成员不可直接访问。

保护继承:基类的 public 和 protected 成员都以 protected 身份出现在派生类中;基类的 private 成员不可直接访问。

私有继承:基类的 public 和 protected 成员,都以 private 身份出现在派生类中;基类的 private 成员不可直接访问。

可以看出无论采用何种继承方式得到的派生类,派生类成员及其友元都不能访问基类的私有成员。且一般情况,保护继承与私有继承在实际编程中极少使用,它们只在技术理论上有意义。

公有继承

公有继承是访问性最高的一种继承,在子类中能完整延续父类成员的访问性,而且对外可见。如果要公有继承一个类,只需继承时在类名前面加上 public 关键字即可。

在公有继承中,派生类成员可以访问继承的基类的 public 部分与 protected 部分,但是不能访问 private 部分,只有基类成员以及基类的友元可以访问 private 部分。

例如:

class Base  
{  
    public:  
        int A;  
};
class D1 : public Base     // 公有继承 Base 类  
{  
    /* …… */  
};
int main()  
{  
    D1 d;  
    d.A = 10;     // 访问 D1 的基类 Base 中的 A 成员,因为是公有继承,所以没问题  
}

编程要求 公有继承 —— 学生信息类

在右侧编辑器中的Begin-End之间补充代码,设计 Student 类,并实现 Set 和 PrintSID 函数,具体要求如下:

Student 类公有成员函数:void PrintSID(),函数输出成员变量 SID 的值,输出格式为:学号:SID。

普通函数:Set(int sid,string name,Student *ptr),它用前两个参数设置 ptr 对象的 SID 和 Name(继承 People 拥有的属性)属性值。

现在已有一个基类 People,它有一个公有成员变量姓名 Name,一个公有成员函数 PrintName(函数的功能是打印出 Name 的值)。

class People  
{  
    public:  
        string Name;  
        void PrintName();  
};  
void People::PrintName()  
{  
    cout << "姓名:" << Name << endl;  
}  
#include "people.h"     // People 类定义在这里面
#include <string>
#include <iostream>

using namespace std;

/**********  Begin **********/
//公有继承 People
class Student : public People
{
	public:
		int SID;
		void PrintSID();
};
/**********  End **********/

void Student::PrintSID()
{
    /********* Begin *********/
    //输出 SID
    cout << "学号:" << SID << endl;
    
    
    /********* End *********/
}

void Set(int sid,string name,Student *ptr)
{
    /********* Begin *********/
    //给 ptr 对象的两个属性赋值
    ptr->SID = sid;
    ptr->Name = name;    
    
    /********* End *********/
}

第2关:保护继承 —— 学生信息类

相关知识

为了完成本关任务,你需要掌握保护继承的使用。

保护继承

保护继承相对于公有继承,访问性有所降低,父类的公有成员在子类中变成了保护成员,也就无法在外部通过一个对象访问父类成员了,但是对于这个子类的子类仍然是可见的(因为可见性只是降到了 protected )。

如果要保护继承一个类,只需继承时在类名前面加上 protected 关键字即可。

例如:

class Base  
{  
    public:  
        int A;  
};
class D1 : protected Base     // 保护继承 Base 类  
{  
    /* …… */  
};
int main()  
{  
    D1 d;  
    d.A = 10;     // 尝试访问 D1 的基类 Base 中的 A 成员,但是由于是保护继承,所以这样做是错误的。  
}  

在保护继承中如果想通过子类访问父类的成员,那就只能在子类中增加一些 get 、set 函数来实现了。

例如:

/* Base类的定义同上 */  
class D1 : protected Base  
{  
    public:  
        void SetA(int a);     // 设置 Base 类中 A 的值  
        int GetA();     // 获取 Base 类中 A 的值  
};
void D1::SetA(int a)  
{  
    A = a;  
}  
int D1::GetA()  
{  
    return A;  
}
int main()  
{  
    Student st;  
    st.SetA(10);     // 将 Base 类的 A 成员设置为 10  
}  

编程要求 保护继承 —— 学生信息类

在右侧编辑器中的Begin-End之间补充代码,采用保护继承设计学生信息类,并实现 Set 和 PrintSID 函数,具体要求如下:

Student 类公有成员函数:void PrintSID(),函数输出成员变量 SID 的值,输出格式为:学号:SID。

普通函数:Set(int sid,string name,Student *ptr),它用前两个参数设置 ptr 对象的 SID 和 Name(继承 People 拥有的属性)属性值。

现在已有一个基类 People,它有一个公有成员变量姓名 Name,一个公有成员函数 PrintName(函数的功能是打印出 Name 的值)。

class People  
{  
    public:  
        string Name;  
        void PrintName();  
};  
void People::PrintName()  
{  
    cout << "姓名:" << Name << endl;  
}  
#include "people.h"     // People 类定义在这里面
#include <string>
#include <iostream>
using namespace std;

/**********  Begin **********/
//保护继承 People
class Student : protected People
{
	public:
		int SID;
		void PrintSID();
    	//添加一个 Set 函数来设置父类的 Name 成员
    	friend void Set(int sid,string name,Student *ptr);
};
// void Student::SetName(string name){
//     Name = name;
// }
/********* End *********/

void Student::PrintSID()
{
    /********* Begin *********/
    //输出学号 SID
    cout << "学号:"<< SID <<endl;
    
    
    /********* End *********/
}

void Set(int sid,string name,Student *ptr)
{
    /********* Begin *********/
    //给 ptr 对象的两个属性赋值
    ptr -> SID = sid;
    ptr -> Name = name;
    
    /********* End *********/
}

第3关:研究生信息类

相关知识

为了完成本关任务,你需要掌握私有继承的使用。

私有继承

私有继承在保护继承的基础上更进一步,访问性进一步降低,父类中的公有成员和保护成员的访问性均降到了私有 private,不仅对外不可见,对这个类的子类也不可见了。

要私有继承一个类,只需继承时在类名前面加上 private 关键字即可。

例如:

/* 继承关系:Base->D1->D2 */  
class Base  
{  
    public:  
        int A;  
};
class D1 : private Base     // 私有继承 Base 类  
{  
    public:  
        F1();  
};  
void D1::F1()  
{  
    A = 10;     // 父类的成员 A 可以看做 D1 类的私有成员,在 D1 类中访问 A 是可行的  
}
class D2 : public D1     // 公有继承 D1  
{  
    public:  
        F2();  
};  
void D2::F2()  
{  
    A = 10;     // 这里就不行了,因为 D1 类私有继承了 Base 类,所以 Base 类的 A 成员对 D2 类就是不可见的。  
}  

同样,如果想在某个类的外部或者它的子类中访问它私有继承的基类的成员,那也只能在这个类中增加 get、set 方法了。

例如:

/* Base类的定义同上 */  
/* 继承关系:Base->D1->D2 */  
class D1 : private Base  
{  
    public:  
        void SetA(int a);     // 设置 Base 类中 A 的值  
        int GetA();     // 获取 Base 类中 A 的值  
};  
void D1::SetA(int a)  
{  
    A = a;  
}  
int D1::GetA()  
{  
    return A;  
}
class D2 : public D1     // 公有继承 D1 类  
{  
    public:  
        void F2();  
}  
void D2::F2()  
{  
    SetA(10);     // 调用 D1 类的 SetA 公有方法设置 Base 类 A 的值  
}  

编程要求 研究生信息类

在右侧编辑器中的Begin-End之间补充代码,设计学生信息类( Student )和设计研究生信息类( Graduate ),Graduate 类公有继承 Student 类,而 Student 类私有继承 People 类,并实现他们的成员函数以及一个普通函数,具体要求如下:

Graduate 类

增加一个成员变量研究方向:int ResearchID,以及一个成员函数:void PrintResearchID(),函数用来输出 ResearchID 的值,输出格式为:研究方向:ResearchID。

Student 类

补充有成员函数:void PrintSID(),函数输出成员变量 SID 的值,输出格式为:学号:SID。

普通函数:Set(int sid,int rid,string name,Graduate *ptr)函数,它用前三个参数设置 ptr 所指对象的三个成员。

People 基类,它有一个公有成员变量姓名 Name,一个公有成员函数 PrintName(函数的功能是打印出 Name 的值),代码如下:

  /* 继承关系:People->Student->Graduate */  
    class People  
    {  
    public:  
        string Name;  
        void PrintName();  
    };  
    void People::PrintName()  
    {  
        cout << Name << endl;  
    }  
#include "people.h" //People类定义在这里面
#include <string>
#include <iostream>
using namespace std;

/********* Begin *********/
//私有继承 People 类
class Student : private People
{
	public:
		int SID;
		void PrintSID();
		//添加一个 Set 函数来设置父类的 Name 成员
		void SetName(string name);
};
void Student::SetName(string name){
    Name = name;
}
/********* End *********/

void Student::PrintSID()
{
    /********* Begin *********/
    //输出学号 SID
    cout<<"学号:"<<SID<<endl;
    
    
    /********* End *********/
}

/********* Begin *********/
// 公有继承 Student 类
class Graduate : public Student
{
	public:
		int ResearchID;
		void PrintResearchID();
		//添加一个 Set 函数来设置父类的 SID 成员
    	friend void Set(string name,int sid,int rid,Graduate *ptr);
		//添加一个 Set 函数来调用父类的 SetName 函数
    	void set(string name);
};
void Graduate::set(string name){
   SetName(name);
}
/********* End *********/

void Graduate::PrintResearchID()
{
    /********* Begin *********/
    //输出研究方向 ResearchID
    cout<<"研究方向:"<<ResearchID<<endl;
    
    
    /********* End *********/
}

void Set(string name,int sid,int rid,Graduate *ptr)
{
    /********* Begin *********/
    //设置 ptr 所指对象的三个成员
    ptr -> set(name);
    ptr -> SID = sid;
    ptr -> ResearchID = rid;
    /********* End *********/
}

第4关:狼人类

相关知识

在前面的关卡中,我们学习的派生类都只有一个基类,称为单继承。除此之外,C++ 也是支持多继承的,即一个派生类可以有两个或多个基类。下面我们就一起来学习多继承的使用。

多继承

C++ 语言支持一个子类同时继承多个父类,就像单继承时一样,继承多个父类也就相当于同时有了多个父类的公有成员和保护成员,而且可以单独为每一个父类指定继承的方式。

因此多继承的优点说可以使一个类实现多个接口,而缺点使容易造成混淆。

如果要继承多个类,只需将父类的类名依次写在子类类名的冒号(:)后面,基类名之间用逗号(,)隔开,每一个基类名前面带上它的访问性关键字。即多继承声明语法如下:

class 派生类名 : 访问控制 基类名1, 访问控制 基类名2, ...  
{  
    成员变量和成员函数的声明  
};  

例如:

/* 继承关系:BaseA->D,BaseB->D */  
class BaseA  
{  
    public:  
        int A;  
};  
class BaseB  
{  
    public:  
        int B;  
};
class D : public BaseA , public BaseB     // 公有继承 BaseA 和 BaseB  
{  
    /* 其他成员 */  
};
int main()  
{  
    D d;  
    d.A = 10;     // 给来自 BaseA 类的成员 A 赋值  
    d.B = 10;     // 给来自 BaseB 类的成员 B 赋值  
}  
多继承访问基类成员

多继承访问基类成员大体与单继承一致,但当继承的多个父类中有同名的成员时,要访问其中一个成员就不能简单的只写成员名了,必须使用作用域运算符(::)来指定是哪一个类的成员。

例如:

/* 继承关系:BaseA->D,BaseB->D */  
class BaseA  
{  
    public:  
        int A;  
};  
class BaseB  
{  
    public:  
        int A;     // 与 BaseA 的 A 成员同名了  
};
class D : public BaseA , public BaseB     / /公有继承 BaseABaseB  
{  
    /* 其他成员 */  
};
int main()  
{  
    D d;  
    d.BaseA::A = 10;     // 使用作用域运算符,给来自 BaseA 类的成员 A 赋值  
    d.BaseB::A = 10;     // 使用作用域运算符,给来自 BaseB 类的成员 A 赋值  
}  

编程要求 狼人类

在右侧编辑器中的Begin-End之间补充代码,实现三个类的设计,其中成员变量和成员函数的访问性可自行设置,具体要求如下:

狼类( Wolf )

    成员变量姓名:string Name

    成员变量爪子锋利度:int Shape

    成员函数:void PrintState(),按照姓名 爪子锋利度格式输出两个成员变量的值。

人类( Human )

    成员变量姓名:string Name

    成员变量智力:int Intell

    成员函数:void PrintState(),按照姓名 智力格式输出两个成员变量的值。

狼人类( Werewolf ),它继承狼类和人类

    成员函数:void SetName(string name),函数用来设置两个基类的成员变量姓名。

    成员函数:void SetState(int shape,int intell),函数用 shape 、intell 两个参数分别设置狼类的爪子锋利度和人类的智力。

    成员函数:void PrintAllState(),函数按照狼类,人类的顺序调用两个基类的 PrintState 函数,输出他们的成员变量值。
#include <string>
#include <iostream>

using namespace std;

/********* Begin *********/
class Wolf
{
	//狼类成员的声明
    public:
	string Name;
    int Shape;
    void PrintState();
};
//狼类成员的定义
void Wolf::PrintState(){
	cout<<"姓名:"<<Name<<",爪子锋利度为:"<<Shape<<endl;
}



class Human
{
	//人类成员的声明
    public :
	string Name;
    int Intell;
    void PrintState();
};
//人类成员的定义
void Human::PrintState(){
	cout<<"姓名:"<<Name<<",智力为:"<<Intell<<endl;
}


// 记得在这里写上要继承的类
class Werewolf : public Wolf,public Human
{
	//狼人类成员的声明
    public:
	void SetName(string name);
    void SetState(int shape,int intell);
    void PrintAllState();
};
//狼人类成员的定义
void Werewolf::SetName(string name){
	Wolf::Name = name;
	Human::Name = name;
}
void Werewolf::SetState(int shape,int intell){
	Shape = shape;
	Intell = intell;
}
void Werewolf::PrintAllState(){
	Wolf::PrintState();
	Human::PrintState();
	
}
/********* End *********/
  • 16
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第4章 MFC实用技术 93 4.1 MFC常用宏应用 94 0188 获取32位整数的低字节和高字节数据 94 0189 将两个16位数组合为一个32位数 94 4.2 MFC常用函数 94 0190 MFC常用调试函数 94 0191 判断某个句柄是否关联一个窗口 95 0192 MFC应用程序信息和管理函数 95 0193 Internet URL解析全局函数 95 4.3 MFC框架技术 96 0194 在类的定义时使其具有运行时类型识别的功能 96 0195 运行时判断某个对象是否是指定的类型 96 0196 禁止文档/视图应用程序运行时显示视图选择窗口 96 0197 多个窗口消息共享同一个消息处理函数 98 0198 遍历对话框中的子控件 99 0199 在程序中捕捉CException及其派生类的异常 100 0200 扩展消息映射宏 100 0201 THIS_FILE的含义 100 0202 为静态文本控件命名 100 0203 在基于对话框的应用程序中添加文档\视图的支持 101 0204 解析浮动状态下工具栏的父窗口 101 4.4 MFC编程技术 101 0205 根据位图资源ID获取位图大小 101 0206 将某个控件对象关联到对话框中的控件资源 102 0207 将一个全局函数指针关联到对话框类的某个方法 102 0208 修改应用程序的图标 102 0209 使用安全数组 103 0210 将子窗口的客户区域映射到父窗口中 103 0211 判断两个时间段的差距 103 0212 重新设置工程名称 103 0213 为dll文件生成lib文件 104 0214 如何将一个工程中的部分资源加到另一个工程中 104 0215 根据句柄获得窗口对象的方法 104 0216 如何共享MSDN 104 0217 从完整的文件名中去除路径 104 0218 从复合字符串中解析子串 105 0219 如何获得应用程序的完整路径 105 0220 修改对话框图标的几种方法 105 0221 将多个具有不同参数的函数赋值为同一个函数指针 105
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值