面向对象程序设计-12-虚函数与多态

声明

题解包含以下内容:

  • (相对)高级的 C++ 模板及语法技巧
  • 仅适用于 C++20 标准的代码
  • 强烈的个人代码风格和 Modern C++ 范式追求
  • 泛滥的标准库函数

换句话说就是(相较于其他公开题解)更低的查重率和更高的使用门槛;请酌情使用。

【id:64】【20分】A. 图形面积(虚函数与多态)

题目描述

编写一个程序,定义抽象基类Shape,在Shape类中定义虚函数area();由它派生出3个派生类:Circle(圆形)、Square(正方形)、Rectangle(矩形)。用虚函数分别计算几种图形面积。
1、要求输出结果保留两位小数。
2、要求用基类指针数组,使它每一个元素指向每一个派生类对象。

输入

测试数据的组数 t

第一组测试数据中圆的半径

第一组测测试数据中正方形的边长

第一组测试数据中矩形的长、宽

第 t 组测试数据中圆的半径

第 t 组测测试数据中正方形的边长

第 t 组测试数据中矩形的长、宽

输出

第一组数据中圆的面积

第一组数据中正方形的面积

第一组数据中矩形的面积

第 t 组数据中圆的面积

第 t 组数据中正方形的面积

第 t 组数据中矩形的面积

样例

输入样例1输出样例1
2
1.2
2.3
1.2 2.3
2.1
3.2
1.23 2.12
4.52
5.29
2.76
13.85
10.24
2.61

Answer

#include <bits/stdc++.h>
using namespace std;

class Shape {

public:
  virtual double area() const = 0;
  virtual ~Shape() {}
};

class Circle : public Shape {
  double radius;

public:
  Circle( double rr ) : radius { rr } {}
  virtual double area() const { return 3.14 * radius * radius; }
};

class Square : public Shape {
  double side;

public:
  Square( double ss ) : side { ss } {}
  virtual double area() const { return side * side; }
};

class Rectangle : public Shape {
  double length, width;

public:
  Rectangle( double ll, double ww ) : length { ll }, width {ww} {}
  virtual double area() const { return length * width; }
};

int main()
{
  size_t t = 0; cin >> t;
  while ( t-- ) {
    vector<unique_ptr<Shape>> shapes( 3 );
    double ll {}, rr {}, ww {};
    cin >> rr; shapes[0] = make_unique<Circle>( rr );
    cin >> ll; shapes[1] = make_unique<Square>( ll );
    cin >> ll >> ww; shapes[2] = make_unique<Rectangle>( ll, ww );
    cout << fixed << setprecision( 2 ) << shapes[0]->area() << endl
      << fixed << setprecision( 2 ) << shapes[1]->area() << endl
      << fixed << setprecision( 2 ) << shapes[2]->area() << endl;
  }
}

【id:180】【20分】B. 汽车收费(虚函数和多态)

题目描述

现在要开发一个系统,实现对多种汽车的收费工作。 汽车基类框架如下所示:

class Vehicle
{ 
protected:
     string no; //编号
public:
    virtual void display()=0; //应收费用
}

以Vehicle为基类,构建出Car、Truck和Bus三个类。

Car的收费公式为: 载客数8+重量2

Truck的收费公式为:重量*5

Bus的收费公式为: 载客数*30

生成上述类并编写主函数,要求主函数中有一个基类指针Vehicle *pv;用来做测试用。

主函数根据输入的信息,相应建立Car,Truck或Bus类对象,对于Car给出载客数和重量,Truck给出重量,Bus给出载客数。假设载客数和重量均为整数。

输入

第一行表示测试次数。从第二行开始,每个测试用例占一行,每行数据意义如下:汽车类型(1为car,2为Truck,3为Bus)、编号、基本信息(Car是载客数和重量,Truck给出重量,Bus给出载客数)。

输出

车的编号、应缴费用

样例

输入样例1输出样例1
4
1 002 20 5
3 009 30
2 003 50
1 010 17 6
002 170
009 900
003 250
010 148

Answer

#include <bits/stdc++.h>
using namespace std;

class Vehicle {
protected:
  string no;

public:
  Vehicle( string _no ) : no { move( _no ) } {}
  virtual ~Vehicle() {}
  virtual void display() const = 0;
};

class Car : public Vehicle {
  int num_load, weight;

public:
  Car( string _no, int n, int w )
    : Vehicle( move( _no ) ), num_load { n }, weight { w } {}
  virtual void display() const {
    cout << no << " " << (num_load * 8 + (weight << 1)) << endl;
  }
};

class Truck : public Vehicle {
  int weight;

public:
  Truck( string _no, int w ) : Vehicle( move( _no ) ), weight { w } {}
  virtual void display() const {
    cout << no << " " << (weight * 5) << endl;
  }
};

class Bus : public Vehicle {
  int num_load;

public:
  Bus( string _no, int n ) : Vehicle( move( _no ) ), num_load { n } {}
  virtual void display() const {
    cout << no << " " << (num_load * 30) << endl;
  }
};

int main()
{
  size_t t = 0; cin >> t;
  Vehicle *pv = nullptr;
  while ( t-- ) {
    int order {}; cin >> order;
    string no; cin >> no;
    if ( order == 1 ) {
      int n, w;
      cin >> n >> w;
      pv = new Car { move( no ), n, w };
    } else if ( order == 2 ) {
      int w;
      cin >> w;
      pv = new Truck { move( no ), w };
    } else {
      int n;
      cin >> n;
      pv = new Bus { move( no ), n };
    }
    pv->display();
    delete pv;
  }
}

【id:65】【20分】C. 支票账户(虚函数与多态)

题目描述

某银行的支票账户分为两类,一类为基本支票账户BaseAccount,另一类为具有透支保护特性的BasePlus支票账户。

BaseAccount支票账户的信息包括:客户姓名(name)、账户(account)、当前结余(balance);BaseAccount支票账户可以执行的操作包括:存款(deposit)、取款(withdraw)、显示账户信息(display)。注意:取款金额不能透支,否则显示出错信息“insufficient”。

BasePlus支票账户除包含BaseAccount的所有信息外,还包括以下信息:透支上限(默认为5000),当前可透支额度(limitSum);BasePlus支票账户可执行的操作与BaseAccount相同,但有两种操作的实现不同:(1)对于取款操作,可以在透支上限范围内透支,超过则显示出错信息“insufficient”;(2)对于显示操作,必须显示BasePlus的其他信息。

请实现BaseAccount类和BasePlus类,其中BasePlus类继承于BaseAccount类,注意BaseAccount账户名称以BA开头,BasePlus账户名称以BP开头。

要求只使用一个基类指针,指向所建立的对象,然后使用指针调用类中的方法。

输入

测试案例组数 t

第一组测试数据:

第一行输入账户信息:姓名 帐号 当前余额

第二行输入四个整数,表示对账户按顺序存款、取款、存款、取款

第二组测试数据:

输出

输出BaseAccount的信息

输出BasePlus的信息

样例

输入样例1输出样例1
4
Tom BA008 1000
1000 2000 1000 1200
Bob BP009 1000
1000 2000 1000 7000
May BA001 2000
500 1000 500 1000
Lily BP002 1000
500 2000 500 3000
insufficient
Tom BA008 Balance:1000
insufficient
Bob BP009 Balance:1000 limit:5000
May BA001 Balance:1000
Lily BP002 Balance:0 limit:2000

Answer

#include <bits/stdc++.h>
using namespace std;

class BaseAccount {
protected:
  string name, account;
  double balance;

public:
  BaseAccount( string nm, string acc, double bal )
    : name { move( nm ) }, account { move( acc ) }, balance { bal } {}
  virtual ~BaseAccount() {}
  virtual void deposit( double amount ) {
    if ( amount < 0 )
      return;
    balance += amount;
  }
  virtual bool withdraw( double amount ) {
    if ( balance >= amount ) {
      balance -= amount;
      return true;
    }
    return false;
  }
  virtual void display() const {
    cout << name << " " << account << " Balance:" << fixed << setprecision( 0 ) << balance << endl;
  }
};

class BasePlus : public BaseAccount {
  double overdraft, limit;

public:
  BasePlus( string nm, string acc, double bal, double lim = 5000 )
    : BaseAccount( move( nm ), move( acc ), bal ), overdraft { lim }, limit { lim } {}
  virtual ~BasePlus() {}
  virtual void deposit( double amount ) {
    if ( amount < 0 )
      return;
    if ( limit < overdraft ) { // 要还钱
      if ( overdraft - limit > amount ) { // 不够还
        limit += amount;
        amount = 0;
      } else { // 够还
        amount -= overdraft - limit;
        limit = overdraft;
      }
    }
    balance += amount;
  }
  virtual bool withdraw( double amount ) {
    if ( balance + limit >= amount ) { // 够取
      if ( balance - amount < 0 ) { // 触发透支
        amount -= balance;
        balance = 0;
      } else { // 余额足够
        balance -= amount;
        amount = 0;
      }
      limit -= amount;
      return true;
    }
    return false;
  }
  virtual void display() const {
    cout << name << " " << account << " Balance:" << fixed << setprecision( 0 ) << balance << " limit:" << limit << endl;
  }
};

int main()
{
  size_t t = 0; cin >> t;
  while ( t-- ) {
    BaseAccount *ptr = nullptr;
    string name, account; double balance {};
    cin >> name >> account >> balance;
    if ( account.find( "BA" ) != string::npos )
      ptr = new BaseAccount( move( name ), move( account ), balance );
    else
      ptr = new BasePlus( move( name ), move( account ), balance );
    for ( int _ = 0; _ < 2; ++_ ) {
      double save {}, fetch {};
      cin >> save >> fetch;
      ptr->deposit( save );
      if ( !ptr->withdraw( fetch ) )
        cout << "insufficient\n";
    }
    ptr->display();
    delete ptr;
  }
}

【id:66】【20分】D. 动物园(虚函数与多态)

题目描述

某个动物园内,有老虎、狗、鸭子和猪等动物,动物园的管理员为每个动物都起了一个名字,并且每个动物都有年龄、体重等信息。每到喂食的时候,不同的动物都会叫唤(speak)。每种动物的叫唤声均不同,老虎的叫唤声是“AOOO”,狗的叫唤声是“WangWang”,鸭子的叫唤声是“GAGA”,猪的叫唤声是“HENGHENG”。

定义一个Animal的基类,Animal类有函数Speak(),并派生老虎、狗、鸭子和猪类,其能发出不同的叫唤声(用文本信息输出叫声)。

编写程序,输入动物名称、名字、年龄,让动物园内的各种动物叫唤。

要求:只使用一个基类指针,指向生成的对象并调用方法。

输入

测试案例的个数

第一种动物的名称 名字 年龄

第二种动物的名称 名字 年龄

输出

输出相应动物的信息

如果不存在该种动物,输出There is no 动物名称 in our Zoo. ,具体输出参考样例输出

样例

输入样例1输出样例1
4
Tiger Jumpjump 10
Pig Piglet 4
Rabbit labi 3
Duck tanglaoya 8
Hello,I am Jumpjump,AOOO.
Hello,I am Piglet,HENGHENG.
There is no Rabbit in our Zoo.
Hello,I am tanglaoya,GAGA.

Answer

#include <bits/stdc++.h>
using namespace std;

class Animal {
  string name, species;
  int age;

public:
  Animal( string nm, string spe, int ag )
    : name { move( nm ) }, species { move( spe ) }, age { ag } {}
  virtual ~Animal() {}
  virtual void Speak() const = 0;
  void display() const {
    cout << "Hello,I am " << name << ",";
  }
};

class Dog : public Animal {
public:
  Dog( string nm, string spe, int ag ) : Animal( move( nm ), move( spe ), ag ) {}
  void Speak() const override { cout << "WangWang.\n"; }
};

class Duck : public Animal {
public:
  Duck( string nm, string spe, int ag ) : Animal( move( nm ), move( spe ), ag ) {}
  void Speak() const override { cout << "GAGA.\n"; }
};

class Pig : public Animal {
public:
  Pig( string nm, string spe, int ag ) : Animal( move( nm ), move( spe ), ag ) {}
  void Speak() const override { cout << "HENGHENG.\n"; }
};

class Tiger : public Animal {
public:
  Tiger( string nm, string spe, int ag ) : Animal( move( nm ), move( spe ), ag ) {}
  void Speak() const override { cout << "AOOO.\n"; }
};

const unordered_set<string_view> zoo { "Dog"sv, "Duck"sv, "Pig"sv, "Tiger"sv };

int main()
{
  size_t t = 0; cin >> t;
  Animal *ani = nullptr;
  while ( t-- ) {
    string name, species; int age {};
    cin >> species >> name >> age;
    if ( !zoo.contains( species ) ) {
      cout << "There is no " << species << " in our Zoo.\n";
      continue;
    }
    if ( species == "Dog"sv )
      ani = new Dog( move( name ), move( species ), age );
    else if ( species == "Duck"sv )
      ani = new Duck( move( name ), move( species ), age );
    else if ( species == "Pig"sv )
      ani = new Pig( move( name ), move( species ), age );
    else ani = new Tiger( move( name ), move( species ), age );
    ani->display(); ani->Speak();
    delete ani; ani = nullptr;
  }
}

【id:295】【20分】E. 进位与借位(虚函数和多态)

题目描述

某小学二年级的数学老师在教学生整数加减法运算时发现:班上的同学可以分成三类,第一类可以正确地完成加减法运算(GroupA);第二类可以正确地完成加法运算,但对于减法运算来说,总是忘记借位的处理(GroupB);第三类总是忘记加法的进位,也总是忘记减法的借位(GroupC)。(提示:小学二年级还没学负数。)

现在请模拟当老师在课堂提问某位同学时,同学会给出的回答。

实现时请基于下面的基类框架:

class Group
{
public:
	virtual int add(int x, int y) = 0; // 输出加法的运算结果
	virtual int sub(int x, int y) = 0; // 输出减法的运算结果
}

构建出GroupA, GroupB和GroupC三个派生类:

并编写主函数,要求主函数中有一个基类Group指针,通过该指针统一地进行add和sub运算。

输入

第一行表示测试次数。从第二行开始,每个测试用例占一行,每行数据意义如下:学生类别(1为第一类学生,2为第二类学生,3为第三类学生)、第一个数、第二个数。

输出

运算后的结果

样例

输入样例1输出样例1
3
1 79+81
2 81-79
3 183+69
160
12
142

Answer

#include <bits/stdc++.h>
using namespace std;

class Group {
public:
  virtual ~Group() {}
  virtual int add( int x, int y ) const = 0;
  virtual int sub( int x, int y ) const = 0;
};

class GroupA : public Group {
public:
  virtual int add( int x, int y ) const {
    return x + y;
  }
  virtual int sub( int x, int y ) const {
    return x - y;
  }
};

class GroupB : public GroupA {
public:
  int sub( int x, int y ) const override {
    string x_str = to_string( x ), y_str = to_string( y );
    int len = min( x_str.size(), y_str.size() );
    string sum;
    for ( int i = 0; i < len; ++i ) {
      auto result = (x_str[x_str.size() - i - 1] -
                     y_str[y_str.size() - i - 1]);
      result = result < 0 ? result + 10 : result;
      sum.push_back( result + '0' );
    }
    sum.append( x_str.size() > y_str.size() ?
      x_str.substr( 0, x_str.size() - len ) :
      y_str.substr( 0, y_str.size() - len ) );
    reverse( sum.begin(), sum.end() );
    return stoi( sum );
  }
};

class GroupC : public GroupB {
public:
  int add( int x, int y ) const override {
    string x_str = to_string( x ), y_str = to_string( y );
    int len = min( x_str.size(), y_str.size() );
    string sum;
    for ( int i = 0; i < len; ++i )
      sum.push_back(
        ((x_str[x_str.size() - i - 1] +
          y_str[y_str.size() - i - 1] - ('0' * 2)) % 10) +
        '0'
      );
    sum.append( x_str.size() > y_str.size() ?
      x_str.substr( 0, x_str.size() - len ) :
      y_str.substr( 0, y_str.size() - len ) );
    reverse( sum.begin(), sum.end() );
    return stoi( sum );
  }
};

int main()
{
  size_t t = 0; cin >> t;
  while ( t-- ) {
    Group *g = nullptr;
    int order {}; cin >> order;
    if ( order == 1 )
      g = new GroupA();
    else if ( order == 2 )
      g = new GroupB();
    else g = new GroupC();

    std::smatch matches;
    string expr; getline( cin, expr );
    std::regex_search( expr, matches, std::regex { R"((\d+)([\+\-\*\/])(\d+))" } );
    if ( matches[2] == '+' )
      cout << g->add( stoi( matches[1] ), stoi( matches[3] ) ) << endl;
    else cout << g->sub( stoi( matches[1] ), stoi( matches[3] ) ) << endl;

    delete g; g = nullptr;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值