Lornaxy

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一、实验目的

1. 理解类的继承和派生机制

2. 掌握派生类的定义和使用

3. 理解和掌握派生类构造函数和析构函数的定义及调用次序

4. 理解和掌握派生类成员的标识和访问中同名覆盖原则、二元作用域分辨符、虚基类的用法

5. 理解和体会类的组合(has-a)和类的继承(is-a)之间的差别,能根据问题场景合理设计多个类之间的 关系

6. 灵活应用继承机制编程解决现实世界的问题

7. 了解C++标准库map容器,掌握其基础用法

二、实验内容

task2:验证性试验

task2.cpp:

#include <iostream>
#include <typeinfo>

// definitation of Graph
class Graph
{
public:
    void draw() { std::cout << "Graph::draw() : just as an interface\n"; }
};


// definition of Rectangle, derived from Graph
class Rectangle : public Graph
{
public:
    void draw() { std::cout << "Rectangle::draw(): programs of draw a rectangle\n"; }
};


// definition of Circle, derived from Graph
class Circle : public Graph
{
public:
    void draw() { std::cout << "Circle::draw(): programs of draw a circle\n"; }
};


// definitaion of fun(): as a call interface
void fun(Graph *ptr)
{
    std::cout << "pointer type: " << typeid(ptr).name() << "\n";
    std::cout << "RTTI type: " << typeid(*ptr).name() << "\n";
    ptr -> draw();//调用的都是 Graph.draw() ,因为将派生类当作基类对象使用时,只能使用作为基类的那一部分接口
    //但若基类的draw()变成virtual void draw()时,就是各调用各的 
}

// test 
int main()
{
    Graph g1;
    Rectangle r1;
    Circle c1;

    // call by object name
    g1.draw();
    r1.draw();
    c1.draw();

    std::cout << "\n";

    // call by object name, and using the scope resolution operator::
    r1.Graph::draw();
    c1.Graph::draw();

    std::cout << "\n";

    // call by pointer to Base class
    fun(&g1);
    fun(&r1);
    fun(&c1); 
}

运行结果如下:

 

 

 

若将基类的draw()函数改为virtual void draw(),运行结果如下:

 

task3:使用类的组合和继承模拟简单的车辆信息管理。

问题场景描述如下: 为了对车量基本信息进行管理,对现实世界车量基本信息抽象后,抽象出Car类、ElectricCar类、 Battery类,它们之间的关系描述如下:ElectricCar类公有继承自Car类,ElectricCar中新增数据成员为 Battery类对象。

类图简化模型如图所示。

基于以下描述设计并实现Battery类

  每个Battery类对象有如下信息:

  车载动力电池容量(capacity)

  要求Battery类提供成员函数实现以下要求:

  带有默认形参值的构造函数。实现Battery类对象的构造,在构造对象时对电池容量 capacity进行初始化,默认参数值为70(单位: kWh)。 返回当前电池容量的函数    get_capacity()。

基于以下描述设计并实现Car类

  每辆汽车有如下信息:

  制造商(maker), 型号(model), 生产年份(year), 行车里程数 (odometers)。

  要求Car类提供成员函数实现以下要求:

  带参数的构造函数。实现Car类对象的构造,并在构造对象时实现对制造商、型号、生产 年份的初始化。初始构造时,行车里程数总是0。 显示车辆信息的函数info()。实现车辆  信息显示输出,输出信息包括:制造商、型号、生 产年份、当前行车里程数。 更新行车里程数的函数update_odometers()。更新后的行车里程数通过参数传递。

  要求 能对参数进行合理性检查,如果更新后的行车里程数值小于当前行车里程数,打印警告 信息,提示更新数值有误。

基于以下描述设计并实现ElectricCar类

  ElectricCar类继承自Car类,新增数据成员电池(battery)。

  其中,battery是Battery类对象。 带参数的构造函数。实现ElectricCar类对象的构造,并在构造时实现对制造商、型号、生产年 份以及新增数据成员电池的初始化。初始构造时,行车里程数总是0。 显示车辆信息info()。实现车辆信息显示输出,包括:制造商、型号、生产年份、当前行车里 程数、当前电池容量。 在主程序中测试设计的类。 要求采用多文件组织代码,文件列表如下: battery.hpp 电池类Battery的定义和实现 car.hpp 汽车类Car的定义和实现 electricCar.hpp 电动汽车类ElectricCar类的定义和实现 electricCar.cpp 电动汽车类ElectricCar类的实现 task3.cpp 主程序。测试Car类对象和Electric类对象。

Car.hpp:

#include<string>
#include<iostream>
using namespace std;

class Car{
private : 
    string maker;//制造商
    string model;//型号 
    int year;//生产年份 
    double odometers;//行车里程数
 
public:
    Car(string maker0, string model0, int year0, double odometers0 = 0);
    void info() const;//展示信息 
    void update_odometers(double odo);//更新行车里程数 

};

Car::Car(string maker0, string model0, int year0, double odometers0): maker{maker0}, model{model0}, year{year0}, odometers{odometers0} {

}

void Car::info() const {
    cout << "maker:\t\t\t" << maker << endl
         << "model:\t\t\t" << model << endl
         << "year:\t\t\t" << year << endl
         << "odometers:\t\t" << odometers << endl;
}

void Car::update_odometers(double odo) {
     if(odo < odometers){
         cout << "更新后的行车里程数值小于当前行车里程数,更新数值有误!" << endl;
     }
     else{
         odometers = odo;
     }
}

Battery.hpp:

#include<string>
using namespace std;

class Battery{
private:
    double capacity;//车载动力电池容量
    
public:
    Battery(double capacity0 = 70);
    double get_capacity() const;


};

Battery::Battery(double capacity0): capacity{capacity0} {

}

double Battery::get_capacity() const {
    return capacity;
}

ElectricCar.hpp:

#include<string>
#include<iostream>
#include "Battery.hpp"
#include "Car.hpp"

using namespace std;

class ElectricCar:public Car{
    private:
        Battery battery;
        
    public:
        ElectricCar(string maker0, string model0, int year0, double odometers0 = 0, double capacity0 = 70);
        void info() const;
};

ElectricCar::ElectricCar(string maker0, string model0, int year0, double odometers0,double capacity0):Car{maker0, model0, year0, odometers0}, battery{capacity0}{}

void ElectricCar::info() const{
     Car::info();
     cout << "capacity:\t\t" << battery.get_capacity() << "-kWh" << endl;
}

  task3.cpp:

#include <iostream>
#include "electricCar.hpp"

int main()
{
    using namespace std;

    // test class of Car
    Car oldcar("Audi", "a4", 2016);
    cout << "--------oldcar's info--------" << endl;
    oldcar.update_odometers(25000);
    oldcar.info();

    cout << endl;

    // test class of ElectricCar
    ElectricCar newcar("Tesla", "model s", 2016);
    newcar.update_odometers(2500);
    cout << "\n--------newcar's info--------\n";
    newcar.info();
}

运行结果如下:

 

 

task4:使用类的继承,模拟简单的机器宠物。

问题场景描述如下:

对机器宠物进行抽象后,抽象出三个简单的类:机器宠物类MachinePets、宠物猫类PetCats、宠物狗 类PetDogs。

设计并实现一个机器宠物类MachinePets。

  每个机器宠物有如下信息:昵称(nickname) 每个机器宠物有如下成员函数: 带参数的构造函数MachinePets(const string s),为机器宠物初始化昵称。 虚函数string talk()为机器宠物派生类提供宠物叫声的统一接口。(关于虚函数,参考实 验任务2)

 

 

设计并实现电子宠物猫类PetCats,该类公有继承自MachinePets。

  每个电子宠物猫类有如下成员 函数: 带参数的构造函数PetCats(const string s),为机器宠物猫初始化昵称。 string talk(),返回电子宠物猫叫声。

 

设计并实现电子宠物狗类PetDogs, 该类公有继承自MachinePets。

  每个电子宠物狗类有如下成 员函数: 带参数的构造函数PetCats(const string s),为机器宠物狗初始化昵称。 string talk(),返回电子宠物狗叫声。

pets.hpp:

#include <iostream>
#include<string>

using namespace std;

class MachinePets{
    private:
        string nickname;
    public:
        MachinePets(const string s):nickname(s){
        }    
        virtual string talk() {
        return "";
    }    
     string get_nickname() const {
        return nickname;
    }
    
};

class PetCats:public MachinePets{
    public:
        PetCats(const string s):MachinePets(s){
        }
        string talk() {
        return "miao wu~";
    }    
        
    
}; 

class PetDogs:public MachinePets{
    public:
        PetDogs(const string s):MachinePets(s){
        }
        string talk() {
        return "wang wang~";
    }    
        
    
}; 

task4.cpp:

#include <iostream>
#include "pets.hpp"

void play(MachinePets *ptr)
{
    std::cout << ptr->get_nickname() << " says " << ptr->talk() << std::endl;
}

int main()
{
    PetCats cat("miku");
    PetDogs dog("da huang");

    play(&cat);
    play(&dog);
}

运行结果如下:

 

 

三、实验结论

①当派生类与基类中有相同成员/函数时,若未强行指名,则通过派生类对象访问的是派生类的同名成员/函数。

②如果要通过派生类的对象访问基类被覆盖的同名成员/函数,需要使用 对象名.基类名::同名成员/函数 来限定(二元作用域分辨符)。

③当将派生类当作基类对象使用时,只能使用作为基类的那一部分接口,但若基类的draw()变成virtual void draw()时,就是各调用各的。

④typeid可以用于获取运行时类型信息。

⑤rbegin和rend是反向迭代器,将内容倒序输出,与迭代器begin和end正好相反。

⑥继承的一些小小的需要注意的写法有如下几种:

例如:

ElectricCar::ElectricCar(string maker0, string model0, int year0, double odometers0, double capacity0): Car{maker0, model0, year0, odometers0}, battery{capacity0} {

}

void ElectricCar::info() const {
    Car::info();//调用基类函数
    cout << "capacity:\t\t" << battery.get_capacity() << "-kWh" << endl;
}

 

newElectricCar.Car::info();//调用基类的同名函数

 

 ⑦在构造函数中,变量的初始化要在声明时就给出,并且按照从右到左的顺序初始化。





 

posted on 2021-11-26 15:03  Lornaxy  阅读(138)  评论(2编辑  收藏  举报