编程打卡: C++ 语言程序设计: 继承与派生: 习题

Posted 松坂制糖厂

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编程打卡: C++ 语言程序设计: 继承与派生: 习题相关的知识,希望对你有一定的参考价值。

编程打卡: C++ 语言程序设计: 继承与派生: 习题

人与学生

问题分析

创建两个类,people 类有两个保护数据成员 age name,行为成员,两个构造函数,一默认,一个有参数,一个设置函数,一个输出函数。student类公有继承people类,有私有数据成员,学号,行为成员,两个构造函数,一个默认,一个有参数,一个设置函数,一个输出函数。在主函数中创建学生,分别调用设置函数和输出函数。

代码实现

#include <iostream>
#include <string>
#include <utility>
using namespace std;
class Student;
class People;
class People 
protected:
    int age;
    string name;
public:
    People();
    People(int a, string str);
    void setValue(int a, string str);
    void display();
;
class Student:public People
protected:
    int id;
public:
    Student();
    Student(int a,string str,int b);
    void setID(int a);
    void displayID() const;
;
People::People() 
    age = 0;
    name = "";

People::People(int a, std::string str) 
    age = a;
    name = std::move(str);

void People::setValue(int a, std::string str) 
    age = a;
    name = std::move(str);

void People::display() 
    cout << "name: " << name << endl << "age: " << age << endl;

Student::Student() 
    age = 0;
    name = "";
    id = 0;

Student::Student(int a, std::string str, int b) 
    age = a;
    name = std::move(str);
    id = b;

void Student::setID(int a) 
    id = a;

void Student::displayID() const 
    cout << "id: " << id << endl;


int main () 
    Student stu1 (17,"Jack",2022);
    Student stu2;
    stu2.setValue(19,"Dick");
    stu2.setID(2023);
    stu1.display();
    stu1.displayID();
    stu2.display();
    stu2.displayID();

运行结果

name: Jack
age: 17
id: 2022
name: Dick
age: 19
id: 2023

多继承

问题分析

学生具有姓名,班级,学号等属性,有上课等行为;教师具有工号,工资等属性,有教课等行为;助教既是学生,又是老师,具有学生和老师的双重属性。

代码实现

#include <iostream>
#include <string>
#include <utility>

using namespace std;
class Student;
class Teacher;
class Student   
protected:
    string name;
    int stu_ID;
    int stu_class;
public:
    Student():stu_ID(0),stu_class(0);
    Student(string n,int a,int b):name(std::move(n)),stu_ID(a),stu_class(b);
    void attend_class ();
;
class Teacher   
protected:
    string name;
    int tea_ID;
    int salary;
public:
    Teacher():tea_ID(0),salary(0);
    Teacher(string n,int a,int b):name(std::move(n)),tea_ID(a),salary(b);
    void teaching();
;
class TA:public Student,public Teacher 
public:
    TA(): Student(), Teacher() ;
    TA(string n,int a,int b,int c,int d): Student(n,a,b), Teacher(n,c,d);
;
void Student::attend_class() 
    cout << stu_ID << " " << name << " from class " << stu_class << " attend class!" << endl;

void Teacher::teaching()  
    cout << tea_ID << " " << name <<  " teaching salary: " << salary << endl;

int main () 
    TA ta1("Dick",2022,1,2022,2000);
    ta1.teaching();
    ta1.attend_class();

运行结果

2022 Dick teaching salary: 2000
2022 Dick from class 1 attend class!

虚基类应用

问题分析

编写动物类animal,受保护数据成员name(名称,string),age(年龄,int),公有函数成员void show(),输出“Animal, 名称, 年龄”;公有派生鱼类fish和兽类beast,鱼类增加受保护数据成员velocity(速度,int),公有函数成员void show(),输出“Fish, 名称, 年龄, 速度”;兽类增加受保护数据成员appetite(食量,int),公有函数成员void show(),输出“Beast, 名称, 年龄, 食量”;鱼类和兽类再公有派生两栖动物类amphibious,无添加数据成员,有公有函数成员void show(),输出 “Fish, 名称, 年龄, 速度”(第一行),“Beast, 名称, 年龄, 食量”(第二行)。每个类均有一个构造函数用于设置该类所有数据成员的属性值。

代码实现

#include <iostream>
#include <string>
#include <utility>
using namespace std;
class Animal    
protected:
    string  name;
    int age;
public:
    Animal() ;
    Animal(string name1, int age1): name(std::move(name1)), age(age1)
 void Show() 
        cout << "Animal," << name << "," << age << endl;
    
;
class Fish :virtual public Animal
protected:
    int velocity;
public:
    Fish(string name1,int age1, int velocity1): Animal(std::move(name1),age1), velocity(velocity1);
    void Show() 
        cout << "Fish," << name << "," << age << "," << velocity << endl;
    
;
class Beast :virtual public Animal
protected:
    int appetite;
public:
    Beast(string name1,int age1, int appetite1): Animal(std::move(name1),age1), appetite(appetite1);
    void Show() 
        cout << "Beast," << name << "," << age << "," << appetite << endl;
    
;
class Amphibious: public Fish, public Beast 
public:
    Amphibious(string name1,int age1,int velocity1, int appetite1):Animal(name1,age1), Fish(name1,age1,velocity1),Beast(name1,age1,appetite1);
    void Show() 
        Fish::Show();
        Beast::Show();
    
;
int main () 
    string s1,s2;
    int a1,a2,v1,v2,ap1,ap2;
    cin >> s1 >> s2 >> a1 >> a2 >> v1 >> v2 >> ap1 >> ap2;
    Amphibious am1(s1,a1,v1,ap1);
    Amphibious am2(s2,a2,v2,ap2);
    am1.Show();
    am2.Show();

运行结果

输入

Joe Dick 12 13 50 40 30 60

输出

Fish,Joe,12,50
Beast,Joe,12,30
Fish,Dick,13,40
Beast,Dick,13,60

以上

C++笔记:继承和多态实例总结

                继承与多态

1.继承与派生的基础

2.派生类的使用

3.多态

4.抽象类

------------------------------------------------------------------------------------------------------------------------------

1.继承与派生的基础

面向对象编程的主要目的之一就是提供可重用的代码。强调可重用性。
使用C语言进行编程时,可以使用C标准的各种函数。C++提供更高层次的重用,如类库。
通过继承可以完成以下工作:
1.可以获得已有类的各种功能。
2.可以给新类添加功能,扩展原有功能。
3.可以修改原有的行为。
 C++中所谓继承就是在一个已经存在类的基础之上建立一个新的类。
 已存在的类被称为基类、父类或超类,新建立的类被称为派生类或子类。
一个新类从已有的类中获得已有类的特征,这被称为类的继承。通过继承子类从父类获得父类的特性。
从已有的类产生一个新的子类,成为类的派生。派生类继承了基类所有的数据成员和方法,并且可以增加自己的数据成员和方法。
一个基类可以派生出去很多个派生类,每个派生类又可以作为基类再派生出新的派生类。如此进行下去,形成了类的继承层次结构。 
 
派生类的声明方式:
从一个基类派生一个类的一般格式为:
class  ClassName: <Access> BaseClassName 
{
       private:  
        ......; //私有成员说明 
       public:  
        ......; //公有成员说明 
       protected:  
        ......; //保护成员说明 
  };
Access表示继承方式:
  public: 表示公有继承, 在派生类和类外可以使用
  private:表示私有继承, 在派生类中使用
  protected:表示保护继承, 只能由类内部使用
public派生
基类中所有成员在派生类中保持各个成员的访问权限
private派生
基类中公有成员和保护成员在派生类中均变为私有的,在派生类中仍可直接使用这些成员,基类中的私有成员,在派生类中不可直接使用。
protected派生
protected对派生类的对象而言,是公开成员,可以访问。
保护派生时,基类中公有成员和保护成员在派生类中均变为保护的和私有的,在派生类中仍可直接使用这些成员,基类中的私有成员,在派生类中不可直接使用。
实际使用中public继承最为常用,其他两种不常用。但大家要知道有这两种继承方式。
 派生类的构成
派生类的成员包括从基类继承的成员和自己增加的新的成员两部分。
从基类获得的成员体现了派生类从基类继承而获得的共性,新增加的成员体现了派生类的个性。不同派生类之间的区别就是这些由新添加的成员导致。
     
 
派生类的构造函数和析构函数
构造函数不能被继承,派生类的构造函数必须调用基类的构造函数来初始化基类成员
派生类构造函数的调用顺序如下:
基类的构造函数(若没有显式定义构造函数则使用编译器自动生成的默认构造函数,默认构造函数内部会调用基类或数据成员为类类型的成员的构造函数)
派生类的构造函数
 
析构顺序则与构造顺序相反。
首先析构派生类
然后是基类析构函数
 

------------------------------------------------------------------------------------------------------------------------------

 2.派生类的使用

1、可以继承基类的成员数据或成员函数。
2、可以增加新的成员变量。
3、可以增加新的成员函数。
4、可以重新定义已有的成员函数。
 
派生类对象与子类对象的子类中的成员可以跟基类的成员名称相同。如相同的数据成员或相同名称
的成员函数。在定义子类对象时,引用跟基类相同的成员时,默认是引用的子类的成员。
 
派生类的对象也是一个基类对象。
is-a关系
派生类与基类之间有一层特殊的is-a(是一种)关系。
我们可以说派生类是基类的一种,而不能说基类是派生类的一种。
因此可以将一个派生类对象赋值给基类对象。由于基类对象不包含派生类的成员,因此只能对基类部分
进行赋值。不能将基类对象赋值给派生类对象。 
基类的指针或引用也可以直接引用子类的对象。
但只能调用子类中跟基类相同的方法。反过来讲子类的指针指向基类的对象则不可以。因为子类具有的方法,基类不一定有。因此可以将一个基类的指针或引用指向派生类对象。
CCurrentTime currentTime;
CTime *p = &currentTime;;
CTime& time = currentTime;
 
通过基类的指针或引用指向派生类对象后,调用的均是基类的成员。

不能访问派生类的成员。
即使派生类重新定义了基类的方法,仍然调用基类的方法。
指针或引用的类型是谁的类型,就访问谁的方法。
 
has-a关系
有一种关系。
汽车与发动机、变速箱的关系就是有一种has-a关系,是一种整体与部分的
关系。可以通过包含来实现。即在类中将另一个类的对象作为本类
的数据成员。
 
构造和析构的顺序。
导致一些性能问题,对象被赋值两次
 
继承虽然可以是实现代码复用,子类通过继承就可以获得基类的功能,但
不要滥用继承。只有明确满足is-a关系的才能使用继承。
满足has-a关系的要将类对象作为其他类的数据成员的方式。
 

------------------------------------------------------------------------------------------------------------------------------

 3.多态

  多态是面向对象的程序设计的关键技术。
  调用同一个函数名,可以根据需要但实现不同的功能。
  编译时的多态性(函数重载)

  运行时的多态性(虚函数)
 
运行时的多态性是指在程序执行之前,根据函数名和参数无法确定应该调用哪一个函数,必须在程序的执行过程中,根据具体的执行情况来动态地确定
通过基类的指针或引用指向派生类对象后,调用的均是基类的成员。不能访问派生类的成员
若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为虚函数,
基类的指针指向派生类对象后, 就可以调用派生类的同名的成员函数。
 
CCurrentTime currentTime;                //派生类对象:currenttime
CTime *p = &currentTime;;     //基类指针ctime指向派生类对象currenttime
CTime& time = currentTime;     //派生类的引用
time.getHour();          //只需要加virtual关键字即可指向派生类对象
 
将基类同名的函数定义为虚函数可以使用C++关键字virtual来实现。
在基类成员函数声明前加virtual关键字。
在派生类中重新定义基类中的虚函数时,可以不用关键字virtual来修饰这个成员函数 。
在程序的执行过程中,依据指针具体指向哪个类对象,或依据引用哪个类对象,才能确定绑定哪个成员函数,实现动态绑定。
 
注意:
1.当在基类中把成员函数定义为虚函数后,在其派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个数必须一一对应,
若函数名相同,但参数的个数不同或者参数的类型不同时,则属于函数的重载,而不是虚函数。
若函数名不同,显然这是不同的成员函数。
2.实现这种动态的多态性时,必须使用基类类型的指针变量,并使该指针指向不同的派生类对象,并通过调用指针所指向的虚函数才能实现动态的多态性。
通过对象名访问虚函数则不会实现动态多态性。
3.在派生类中没有重新定义虚函数时,与一般的成员函数一样,当调用这种派生类对象的虚函数时,则调用其基类中的虚函数。
4.可把析构函数定义为虚函数,但是,不能将构造函数定义为虚函数。

  因为构造函数不能被继承,自己完成初始化
5.多态时通过虚函数动态绑定实现。内部泽是通过虚函数表实现。 调用时的执行速度要慢。虚函数表是一张表,其内部存储很多虚函数的地址。基类对象有一张,子类也有一张,
初始时子类继承基类的表,若子类覆盖(重写)了基类的虚函数,则将子类的虚函数表对应项目替换为子类虚函数指针。大概了解,随着理解的加深,可以深入学习一下。

------------------------------------------------------------------------------------------------------------------------------

 4.抽象类:对应于纯虚函数

纯虚函数:在基类中仅仅给出声明,不对虚函数实现定义,而是在派生类中实现。这个虚函数称为纯虚函数。
纯虚函数需要在声明之后加个=0

class    <基类名>
{
  virtual <类型><函数名>(<参数表>)=0; ......
};

含有纯虚函数的类被称为抽象类只能作为派生类的基类,不能定义对象,但可以定义指针。
在派生类实现该纯虚函数后,定义抽象类对象的指针,并指向或引用子类对象。

1.在定义纯虚函数时,不能定义虚函数的实现部分。
2.在没有重新定义这种纯虚函数之前,不能定义是不能调用这种函数的。

抽象类的唯一用途是为派生类提供基类,纯虚函数的作用是作为派生类中的成员函数的基础,并实现动态多态性。
接口和抽象类的区别
  C++中我们一般说的接口,表示对外提供的方法,提供给外部调用。是沟通外部跟内部的桥梁。也是以类的形式提供的,

  但一般该类只具有成员函数,不具有数据成员。
  抽象类可以既包含数据成员又包含方法。

------------------------------------------------------------------------------------------------------------------------------

 



























































































































以上是关于编程打卡: C++ 语言程序设计: 继承与派生: 习题的主要内容,如果未能解决你的问题,请参考以下文章

编程打卡: C++ 语言程序设计

c++面向对象的程序设计

编程打卡:C++语言程序设计

浅析C++继承与派生

C++笔记:继承和多态实例总结

C++的探索路12继承与派生之高级篇--派生类与赋值运算符及多重继承