西安石油大学C++上机实验汇总

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 西安石油大学C++上机实验汇总

考试题难度就像第三章第五章课后题的难度

基础知识一定要掌握!!!

上机一:类与对象程序设计(2 学时)

上机目的

  1. 掌握声明类的方法。
  2. 掌握类和类的成员的概念以及定义对象的方法。
  3. 掌握构造函数和析构函数的使用方法

实验目的

掌握声明类的方法,类和类的成员的概念以及定义对象的方法。

掌握用类和对象编制基本对象的程序的方法。

掌握构造函数和析构函数的使用方法。

上机内容

1.进行类的设计和使用,练习构造函数的重载和析构函数的使用方法;练

习具有不同访问权限的成员的访问方式;

2.测试章节例题,完成教材 P101:3.34,3.35。

题目 1:编写一个基本账户类,成员变量包括:账号,储户姓名,和存款余

额,成员函数包括:存款和取款。编写一个测试程序进行测试。

题目 2:设计一个长方形类,成员变量包括长度和宽度,成员函数除包括计

算周长和计算面积外,还包括用 Set 方法设置长和宽,以及用 get 方法来获取长

方形的长和宽。

实验内容

P101:3.34, 3.35

题目 1:编写一个基本账户类,成员变量包括:账号,储户姓名,和存款余额,成员函数包括:存款和取款。编写一个测试程序进行测试

思路:

编辑头文件。在弹出的 Account.h 头文件中添加 Account 类的代码。如图
1-5 所示。详细代码如下:
步骤 a:声明程序设计所需要包含的头文件:
#include <iostream.h>
#include <string.h>
步骤 b:根据题目要求:定义账户类:Account 类:
class Account
{
……
7
8
};
步骤 c:在 Account 类中定义成员变量:
private:
long accountNo; //账户
char name[30]; //储户姓名
float money; //账户余额
步骤 d:定义成员函数:
构造函数:
(这些都是在account中的东西(变量))
Account(long a,char *n,float b): accountNo(a),money(b){
 strcpy(name,n);
}
析构函数:
~Account(){}
存款成员函数:
void Add(const float x){
money=money+x;
}
取款成员函数:
void Sub(const float x){
money=money-x;
}
输出成员函数:
void Print(void)const{
cout<<"姓名:"<<name<<endl;
cout<<"账户:"<<accountNo<<endl;
cout<<"余额:"<<money<<endl;
}
4)添加源文件代码。在 Account.cpp 文件中添加主程序代码,详细代码如下:
a:测试主程序详解:
#include "Account.h" //包含 Account.h 头文件(此时应该注意“”这个符号应该出现在知道头文件位置的地方当不确定Account在何处时会报错)
void main()
{
//一个萝卜一个坑,对定义的对象类进行赋值初始化定义
Account a1(1000000,"阿斌",100); //定义对象,对象名为 a1,其账号为 1000000,储户姓名为阿斌,账户余额为 100 元
Account a2(1000001,"阿豪",100); //定义对象,对象名为 a2,其账号为 1000001,储户姓名为阿豪,账户余额为 100 元
cout<<"名为阿斌的用户向账户存款 50 元"<<endl;
//此时程序的意思就是对构造的a1对象进行Add函数操作
a1.Add(50); //对象 a1 调用 Add 存款成员函数向账户存款 50 元
//此时程序的意思就是对构造的a1对象进行Print函数操作
a1.Print(); //对象 a1 调用 Print 成员函数输出显式账号、姓名、余额
cout<<endl; //换行
 cout<<"名为阿豪的用户从账户取款款 50 元"<<endl;
a2.Sub(50); //对象 a2 调用 Sub 取款成员函数从账户取款 50 元
a2.Print(); //对象 a2 调用 Print 成员函数输出显式账号、姓名、余额
}

完整源码

#include<iostream>
#include<string>
using namespace std;
class zhanghu {
public:
 std::string name;
 long int ZH;
 int CKYE;
 zhanghu(){}
 zhanghu(string _name, long int y = 12345678, int z = 100) {
  name = _name;
  ZH = y;
  CKYE = z;
 }
 int CK(int a) {
  CKYE += a;
  return CKYE;
 }
 int QK(int b) {
  CKYE -= b;
  return CKYE;
 }
 ~zhanghu()
 {}
};
int main() {
 int n, o, p;
 zhanghu m("chenbochao", 12345678, 100);
 cout << "您的姓名是" << m.name << endl << "账号是" << m.ZH << endl << "余额是" << m.CKYE << endl;
 cout << "--------------------------" << endl;
 cout << "请输入操作" << endl <<"1存款" << endl << "2取款" << endl;
 cout << "--------------------------" << endl;
 while (scanf("%d", &n) != EOF) {
  if (n == 1) {
   cout << "请输入存款金额:" << endl;
   cin >> o;
   cout << "--------------------------" << endl;
   m.CK(o);
   cout << "您的姓名是" << m.name << endl << "账号是" << m.ZH << endl << "余额是" << m.CKYE << endl;
   cout << "--------------------------" << endl;
  }
  if (n == 2) {
   cout << "请输入取款金额:" << endl;
   cin >> p;
   cout << "--------------------------" << endl;
   m.QK(p);
   cout << "您的姓名是" << m.name << endl << "账号是" << m.ZH << endl << "余额是" << m.CKYE << endl;
   cout << "--------------------------" << endl;
  }
 }
 return 0;
}
#include<iostream>
using namespace std;
class Complex
{
private:
 double real;
 double image;
public:
 friend Complex operator+(const Complex& x, const Complex& y);
 friend Complex operator-(const Complex& x, const Complex& y);
 Complex()
 {}
 Complex(double x,double y):real(x),image(y)
 {}
 ~Complex()
 {}
 Complex operator/(const Complex& x) const;
 Complex operator*(const Complex& y) const;
 void show();
};
inline Complex operator+(const Complex& x, const Complex& y)
{
 return Complex(x.real + y.real, x.image + y.image);
}
inline Complex operator-(const Complex& x, const Complex& y)
{
 return Complex(x.real - y.real, x.image - y.image);
}
inline Complex Complex::operator/(const Complex& x) const
{
 double m;
 m = x.real * x.real + x.image * x.image;
 return Complex((real * x.real - image * x.image) / m, (image * x.image - real * x.real)/m);
}
inline Complex Complex::operator*(const Complex& y) const
{
 return Complex(real * y.real - image * y.image, real * y.image + image * y.real);
}
void Complex::show()
{
 cout << "(" << real << "," << image <<"i)" << endl;
}
void main()
{
 Complex a(1, 2), b(2, 3), c, d, e, f;
 c = a + b;
 cout << "两个复数之和为:" << endl;
 c.show();
 d = a - b;
 cout << "两个复数之差为;" << endl;
 d.show();
 e = a * b;
 cout << "两个复数的乘积为:" << endl;
 e.show();
 f = a / b;
 cout << "两个复数的商为:" << endl;
 f.show();
}

题目 2:设计一个长方形类,成员变量包括长度和宽度,成员函数除包括计算周长和计算面积外,还包括用 Set 方法设置长和宽,以及用 get 方法来获取长

方形的长和宽

1)创建头文件。在工程主界面左上角点击 File 菜单,在弹出的 New 对话框
中选择 C/C++ Header File 选项,新建头文件命名为 Rectangle.h。用来设计长方形类。
2)编辑头文件。在弹出的 Rectangle.h 头文件中添加 Rectangle 类的代码。
步骤 a:声明程序设计所需要包含的头文件:
#include <iostream.h>
步骤 b:根据题目要求:定义长方形类:Rectangle 类:
class Rectangle
{
……
};
步骤 c:在 Rectangle 类中定义成员变量:
private:
float length; //长
float width; //宽
步骤 d:定义成员函数:
构造函数:
Rectangle(float len = 1, float wid = 1):length(len), width(wid){}
析构函数:
~ Rectangle (){}
//定义函数功能
计算长方形周长的成员函数:
float Perimeter(){
return 2*(length + width);
}
计算长方形面积的成员函数:
double Area() {
return length*width;
}
设置长方形长的成员函数:
void SetLength(float len)
{
length = len;
}
设置长方形宽的成员函数
void SetWidth(float wid)
{
width = wid;
}
获取长方形长的成员函数
float GetLength()
{
return length;
}
获取长方形宽的成员函数
float GetWidth()
{
return width;
}
步骤 e:程序编写示例
3)创建源文件。在工程主界面左上角点击 File 菜单,在弹出的 New 对话框
中选择 C++Source File 选项,新建头文件命名为 Rectangletest.cpp。用来测试长
方形类。
4)编辑源文件。在弹出的 Rectangletest.cpp 头文件中添加测试主程序代码。
a.详细代码:
#include "Rectangle.h" //包含 Rectangle.h 头文件
void main()
{
Rectangle Rec1, Rec2(10, 10);
cout<<"长:"<<Rec1.GetLength()<<" 宽:"<<Rec1.GetWidth()<<endl;
cout<<"周长:"<<Rec1.Perimeter()<<" 面积:"<<Rec1.Area()<<endl<<endl;
Rec1.SetLength(5);
Rec1.SetWidth(8);
cout<<"长:"<<Rec1.GetLength()<<" 宽:"<<Rec1.GetWidth()<<endl;
cout<<"周长:"<<Rec1.Perimeter()<<" 面积:"<<Rec1.Area()<<endl<<endl;
cout<<"长:"<<Rec2.GetLength()<<" 宽:"<<Rec2.GetWidth()<<endl;
cout<<"周长:"<<Rec2.Perimeter()<<" 面积:"<<Rec2.Area()<<endl<<endl;
}
#include<iostream>
using namespace std;
class CF {//定义CF类
public:
 int C;
 int K;
 CF()
 {}
 //定义函数功能
 void set(){
  cout << "请输入长度" << endl;
  cin >> C;
  getchar();
  cout << "请输入宽度" << endl;
  cin >> K;
 }
 void get() {
  cout << "长度为:" << C << endl;
  cout << "宽度为:" << K << endl;
 }
 int ZC() {
  int c;
  c = 2 * (C + K);
  return c;
 }
 int MJ() {
  int c;
  c = C * K;
  return c;
 }
 ~CF()
 {}
};
int main() {
 int a;
 int c, d;
 CF m;
 m.set();
 cout << "--------------------------" << endl;
 m.get();
 cout << "--------------------------" << endl;
 cout << "请选择操作:" << endl << "1:求周长" << endl << "2:求面积" << endl;
 cout << "--------------------------" << endl;
 while (scanf("%d", &a) != EOF) {
  if (a == 1) {
   cout << "周长是" << m.ZC() << endl;
  }
  else if (a == 2) {
   cout << "面积是" << m.MJ() << endl;
  }
 }
 return 0;
}
#include<string>
#include<iostream>
using namespace std;
#define pi 3.1415926
class Shap
{
public:
 virtual void area() = 0;
 virtual void volume() = 0;
};
class Cube :public Shap//正方体
{
private:
 float side_length;
public:
 Cube(float Side_length) :side_length(Side_length) {}
 void area()
 {
  cout << "正方体表面积是:" << 6 * pow(side_length, 2) << endl;
 }
 void volume()
 {
  cout << "正方体体积是:" << pow(side_length, 3) << endl;
 }
};
class Cylinder :public Shap//圆柱体
{
private:
 float r;
 float h;
public:
 Cylinder(float H, float R) :r(R), h(H) {}
 void area()
 {
  cout << "圆柱体表面积是:" << 2 * pi * pow(r, 2) + 4 * pi * r * h << endl;
 }
 void volume()
 {
  cout << "圆柱体体积是:" << pi * pow(r, 2) * h << endl;
 }
};
class Sphere :public Shap//球体
{
private:
 float r;
public:
  Sphere(float R) :r(R) {}
 void area()
 {
  cout << "球体表面积是:" << 4 * pi * pow(r, 2) << endl;
 }
 void volume()
 {
  cout << "球体体积是:" << 4 * pi * pow(r, 3) / 3 << endl;
 }
};
void main(void)
{
 Shap* p;
 int n;//形状
 char op;//操作
 float side_length;
 float r;
 float h;
 bool flag = 1;
 cout << "请选择形状:" << endl;
 cout << "1:正方体" << endl;
 cout << "2:圆柱体" << endl;
 cout << "3:球体" << endl;
 // cin>>n;
loop: cout << "-------------------------------------" << endl;
 while (cin >> n)
 {
  switch (n)
  {
  case 1://正方体
    cout << "请输入边长:";
   cin >> side_length;
   p = new Cube(side_length);
   cout << "-------------------------------------" << endl;
   cout << "请选择操作:" << endl;
   cout << "a:表面积" << endl;
   cout << "b:体积" << endl;
   cout << "c:返回" << endl;
   while (cin >> op)
   {
    switch (op)
    {
    case 'a':
     p->area();
     break;
    case 'b':
     p->volume();
     break;
    case 'c':
     goto loop;
    default:
     cerr << "输入错误!" << endl;
    }
   }
   break;
  case 2://圆柱体
   cout << "请输入半径:";
   cin >> r;
   cout << endl;
   cout << "请输入高:";
   cin >> h;
   p = new Cylinder(r, h);
   cout << "-------------------------------------" << endl

思考题

分析总结上述程序设计实现的不足之处,如账户类中当取款金额大于账户原

有余额时,应如何操作?如长方形类结果输出采用函数调用方式是否更为方便?

如若要实现两个长方形对象的面积加和时又该如何进行程序设计?请针对程序

中的不足之处进行完善修改,并思考如何进行功能拓展。

上机二:友元和运算符重载程序设计(2 学时)

实验目的:

了解友元函数的概念。

掌握友元函数的定义和使用方法。

了解运算符重载的概念和使用方法。

掌握几种常用的运算符重载的方法。

实验内容

编写一个时间类,实现时间的加、减运算并输出。

上机内容

1.练习友元函数的定义和运算符重载的方法;

2.测试章节例题,设计运算符重载的复数类,实现常用复数运算操作,分别重载为类的成员函数和重载为类的友元函数,并编写一个测试程序进行测试。

(4)编辑头文件。在弹出的 Complex.h 头文件中添加 Complex 类的代码,

完成如下。

步骤 a:声明程序设计所需要包含的头文件:
#include <iostream.h>
步骤 b:根据题目要求:定义复数类:Complex 类:
class Complex
{
……
};
步骤 c:定义类中成员变量:
private:
double real; //实部
double image; //虚部
步骤 d:将加法运算符重载为类的友元函数:
friend Complex operator+(const Complex &x,const Complex &y); //类内函
数说明
23
inline Complex operator+(const Complex &x,const Complex &y); //类外函
数实现
{
return Complex(x.real+y.real,x.image+y.image);
}
步骤 e:将减法运算符重载为类的友元函数:
friend Complex operator-(const Complex &x,const Complex &y); //类内函数
说明
inline Complex operator-(const Complex &x,const Complex &y) //类外函
数实现
{
return Complex(x.real-y.real,x.image-y.image);
}
步骤 f:定义成员函数:
构造函数:
Complex(double x=0, double y=0):real(x),image(y){}
析构函数:
~ Complex (){}
将乘法运算符重载为类的成员函数:
Complex operator*(const Complex &x) const; //类内函数声明
inline Complex Complex::operator*(const Complex &x) const //类外函数实
{
return Complex(real*x.real-image*x.image,real*x.image+image*x.real);
}
将除法运算符重载为类的成员函数:
Complex operator/(const Complex &x) const; //类内函数声明
inline Complex Complex::operator/(const Complex &x) const //类外函数实现
24
{
double m;
m=x.real*x.real+x.image*x.image;
 return Complex((real*x.real+image*x.image)/m,
(image*x.real-real*x.image)/m);
}
输出成员函数:
void Show(); //类内函数声明
void Complex::Show() //类外函数实现
{
cout<<"("<<real<<","<<image<<"i)"<<endl;
}
(6)添加源文件代码。在 Complextest.cpp 文件中添加主程序代码,详细代
码如下:
#include "Complex.h" //包含 Complex.h 头文件
void main()
{
Complex a(1,2),b(2,3),c,d,e,f;
c=a+b;
cout<<"两个复数之和为:"<<endl;
c.Show();
d=a-b;
cout<<"两个复数之差为:"<<endl;
d.Show();
 e=a*b;
cout<<"两个复数的乘积为:"<<endl;
e.Show();
26
 f=a/b;
cout<<"两个复数的除法运算结果为:"<<endl;
f.Show();
}

思考题

分析总结上述程序设计实现的不足之处,并思考复数类其他运算符(如+=,==等)重载如何实现

实验内容

编写一个时间类,实现时间的加、减运算并输出。

#include <iostream>
#include <iomanip>
using namespace std;
class Time {
 private://定义时间类中的私有对象hour minute second
  int hour;
  int minute;
  int second;
 public:
  Time(int h = 0, int m = 0, int s = 0) : hour(h), minute(m), second(s) {};
  Time(const Time &other) : hour(other.hour), minute(other.minute), second(other.second) {};
  friend istream &operator>>(istream &input, Time &other) {
   input >> other.hour >> other.minute >> other.second;
   return input;
  }
  friend ostream &operator<<(ostream &output, const Time &other) {
   output << setw(2) << setfill('0') << other.hour << ":" << setw(2) << setfill('0') << other.minute << ":" << setw(
              2) << setfill('0') << other.second << endl;
   return output;
  }
  const Time operator++(int) {
   Time tmp(*this);
   int x = hour * 3600 + minute * 60 + second;
   x++;
   hour = x / 3600;
   x %= 3600;
   minute = x / 60;
   second = x % 60;
   return tmp;
  }
  const Time operator--(int) {
   Time tmp(*this);
   int x = hour * 3600 + minute * 60 + second;
   x--;
   hour = x / 3600;
   x %= 3600;
   minute = x / 60;
   second = x % 60;
   return tmp;
  }
  Time operator+=(const Time &other) {
   second += other.second;
   minute += other.minute;
   hour += other.hour;
   if (second >= 60) {
    minute += second / 60;
    second %= 60;
   }
   if (minute >= 60) {
    hour += minute / 60;
    minute %= 60;
   }
   if (hour >= 24) {
    hour %= 24;
   }
   return *this;
  }
  Time operator-=(const Time &other) {
   if (second < other.second) {
    second += 60;
    second -= other.second;
    minute--;
   } else {
    second -= other.second;
   }
   if (minute < other.minute) {
    minute += 60;
    minute -= other.minute;
    hour--;
   } else {
    minute -= other.minute;
   }
   if (hour < other.hour) {
    hour += 24;
    hour -= other.hour;
   } else {
    hour -= other.hour;
   }
   return *this;
  }
  Time &operator++() {
   int x = hour * 3600 + minute * 60 + second;
   x++;
   hour = x / 3600;
   x %= 3600;
   minute = x / 60;
   second = x % 60;
   return *this;
  }
  Time &operator--() {
   int x = hour * 3600 + minute * 60 + second;
   x--;
   hour = x / 3600;
   x %= 3600;
   minute = x / 60;
   second = x % 60;
   return *this;
  }
};
int main() {
 Time t1, t2;
 cin >> t1;
 cin >> t2;
 cout << (t1 += (t2++));
 cout << (t1 -= t2);
 cout << ++t2;
 cout << (t2 += (t1--));
 cout << --t1;
 cout << (t2 -= t1);
}

上机三:继承和派生程序设计(2 学时)

实验目的

了解继承在面向对象程序设计中的重要作用。

理解继承与派生的概念。

掌握通过继承派生出一个新类的方法。

掌握虚基类的作用和用法。

实验内容

P169:5.19, 5.22

上机内容

先设计一个基本账户类,再通过继承基本账户类设计一个储蓄账户类,储蓄

账户类中增加密码、地址、最小余额和利率等成员变量,并增加一些银行账户经

常用到的成员函数。要求:

(1) 类中的成员函数具有输入、输出储户上述信息的功能;

(2) 将账号设计成不可更改;

(3) 修改密码时要提供原始密码。

#include<string>
#include<iostream>
using namespace std;
class Bank
{
private:
30
31
char name[20];//姓名
 long id;//账号
long balance;//余额
public:
Bank(char *Name,long Id,long Balance)//const变量需要用初始化表初始化
{
strcpy(name,Name);
id=Id;
balance=Balance;
}
string GetName(){return name;}//获取姓名
long GetId(){return id;}//获取账号
long GetBalance(){return balance;}//获取余额
long cunkuan(long money)//存款
{
balance+=money;
return balance;
}
long qukuan(long money)//取款
{
balance-=money;
return balance;
}
string change_name()//修改姓名
{
 char new_name[20];
 cout<<"请输入修改后的姓名:"<<endl;
 cin>>new_name;
 strcpy(name,new_name);
 cout<<"修改成功!"<<endl;
 return name;
}
};
class SaveBank:public Bank
{
private:
char password[20];//密码
char address[20];//住址
long minimum_balance;//最小余额
32
float annual_interest_rate;//年利率
public:
SaveBank(char *Name,long Id,long Balance,char *Password,char
*Address,long Minimum_balance,float
Annual_interest_rate):Bank(Name,Id,Balance),minimum_balance(Minimum_balance)
,annual_interest_rate(Annual_interest_rate)
{
strcpy(password,Password);
strcpy(address,Address);
}
string GetPassword(){return password;}//获取密码
 string GetAddress(){return address;}//获取住址
long Get_minimum_balance(){return minimum_balance;}//获取最小余额
float Get_annual_interest_rate(){return annual_interest_rate;}//获取年利率
string change_password()//修改密码
{
 char new_password[20];
 char number[20];
cout<<"请输入原始密码:"<<endl;
cin>>number;
if((strcmp(number,password))==0)
{cout<<"请输入新密码:"<<endl;
cin>>new_password;
strcpy(password,new_password);
cout<<"修改成功!"<<endl;
}
else
{cerr<<"原始密码错误!请重新选择操作!"<<endl;}
return password;
}
string change_address()//修改地址
{
 char new_address[20];
 cout<<"请输入修改后的地址:"<<endl;
 cin>>new_address;
strcpy(address,new_address);
 cout<<"修改成功!"<<endl;
33
 return address;
}
void getbank()//获取客户账户信息
{
cout<<"姓名:"<<GetName()<<endl;
cout<<"账号:"<<GetId()<<endl;
cout<<"余额:"<<GetBalance()<<endl;
cout<<"密码:"<<GetPassword()<<endl;
cout<<"地址:"<<GetAddress()<<endl;
cout<<"最小余额:"<<Get_minimum_balance()<<endl;
cout<<"利率:"<<Get_annual_interest_rate()<<endl;
}
};
(4)添加源文件代码。在 bank.cpp 文件中添加主程序代码,如图 6 所示。
代码如下:
#include "stdafx.h"
#include<iostream>
#include<stdlib.h>
#include <stdio.h>
#include "bank.h"
using namespace std;
int main(void)
{
char name[20];
long id;
long balance;
char password[20];
char address[20];
long minimum_balance;
float annual_interest_rate;
int op; //操作
long money; //存款 or 取款数额
cout<<"请输入客户信息"<<endl;
cout<<"————————————————————————————————
————————"<<endl;
cout<<"姓名:";
cin>>name;
cout<<"账号:";
cin>>id;
cout<<"余额:";
cin>>balance;
cout<<"密码:";
35
cin>>password;
cout<<"地址:";
cin>>address;
cout<<"最小余额:";
cin>>minimum_balance;
cout<<"年利率:";
cin>>annual_interest_rate;
SaveBank
jianghan(name,id,balance,password,address,minimum_balance,annual_interest_rate);
cout<<"———————————请输入操作
———————————————————————"<<endl;
cout<<"1:打印客户信息"<<endl;
cout<<"2:存款"<<endl;
cout<<"3:取款"<<endl;
cout<<"4:修改密码"<<endl;
 cout<<"5:修改姓名"<<endl;
 cout<<"4:修改地址"<<endl;
while(cin>>op)
{
switch(op)
{
case 1:
jianghan.getbank();
break;
case 2:
cout<<"请输入存款金额:"<<endl;
cin>>money;
jianghan.cunkuan(money);
cout<<"当前余额:"<<jianghan.GetBalance()<<endl;
break;
case 3:
cout<<"请输入取款金额:"<<endl;
cin>>money;
jianghan.qukuan(money);
cout<<"当前余额:"<<jianghan.GetBalance()<<endl;
break;
case 4:
jianghan.change_password();
break;
case 5:
36
jianghan.change_name();
break;
case 6:
jianghan.change_address();
break;
default:
cerr<<"操作错误!"<<endl;
}
}
return 0;
}

实验内容

P169:5.19, 5.22

5.19

5.19 设计一个动物类,它包含一些动物的属性,如名称、大小、重量等,动物可以跑或走。然后设计一个鸟类,除了动物的基本属性外,它还有自己的羽毛、翅膀等。鸟除了跑或走外,它还可以飞翔。为了继承动物类的特性,鸟类应该继承动物类。编写一个程序来测试所设计的鸟类能否实现预定的功能。

#include <iostream>
#include<string>
using namespace std;
class Animal //声明基类Animal
{public:
Animal(string n,char s,double w) //基类构造函数
{
name=n;
size=s;
weight=w;
}
~Animal( ){ } //基类析构函数
void Run();
void Walk();
protected: //私有部分
string name;
char size;
double weight;
};
class Bird: public Animal //声明派生类Animal
{public:
Bird(string n,char s,double w,string f,string wi):Animal(n,s,w)
{
feather=f; //在函数体中只对派生类新增的数据成员初始化
wing=wi;
}
~Bird( ){ } //派生类析构函数
void show( );
void Fly();
private: //派生类的私有部分
string feather;
string wing;
};
void Animal::Run()
{
cout<<"I can run!"<<endl;
}
void Animal::Walk()
{
cout<<"I can walk!"<<endl;
}
void Bird::show()
{
cout<<"name:"<<name<<endl;
cout<<"size:"<<size<<endl;
cout<<"weight:"<<weight<<endl;
cout<<"feather:"<<feather<<endl;
cout<<"wing:"<<wing<<endl;
}
void Bird::Fly()
{
cout<<"I can fly!"<<endl;
}
void main()
{
Bird bird("乌鸦",'L',50,"黑色羽毛","斑点翅膀");
bird.show ();
bird.Run ();
bird.Walk ();
bird.Fly ();
}
//我是这样的
public class Animal{
  private String name;
  private double weight;
  private int length;
  privateString sex;
  public void run(){
  System.out.println("the animal is running!");来源:考试大
  }
  public void jump(){
  System.out.println("the animal is jumping!");
  }
  public void walk(){
  System.out.println("the animal is walking!");
  }
  }

c++ 管理动物园动物

Problem Description

动物园(Zoo)有三种动物(Animal):猫(Cat)、狗(Dog)、老虎(Tiger),显示所有动物的信息。
(1)定义5个类,动物园(Zoo)、动物(Animal)、猫(Cat)、狗(Dog)、老虎(Tiger)。
Animal是抽象基类,Cat、Dog是Animal的派生类,Tiger是Cat的派生类。
Animal中有2个数据成员。一个是动物的姓名,另一个是动物的年龄。有1个成员函数,是纯虚函数void Display(),用来显示动物的姓名和年龄。
Cat、Dog和Tiger中有3个成员函数。构造函数、析构函数和重写的函数void Display()。
Zoo中有3个数据成员。分别是:动物园最多能容纳的动物数(max)、动物园当前动物数(num)、指向Animal的数组(Animal** asp)。
Zoo中有4个成员函数。分别是:
构造函数(Zoo(int max)),完成这几个任务,把构造函数参数的值赋给对象的max,动物园当前动物数为0,
new一个Animal的数组并且都初始化为NULL。
析构函数,释放数组空间。
添加动物函数(void Add(Animal* ap)),把ap指向的动物添加到动物园数组中。
显示动物园所有动物信息函数(void ShowAll())。
(2)在main函数中创建1个最大数为5的动物园,再创建2个猫,2个狗,1个老虎,然后把它们添加到动物园,最后显示所有动物的信息。
请完成下面的程序:
————————————————
版权声明:本文为CSDN博主「爱吃月饼的兔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dimrhyme/article/details/121961164
int main()
{
Zoo z(5);
Animal* pa = new Cat (“AAA”, 3);
z.Add(pa);
//delete pa;
pa = new Dog(“BBB”, 1);
z.Add(pa);
//delete pa;
pa = new Tiger (“CCC”, 2);
z.Add(pa);
//delete pa;
z.ShowAll();
return 0;
}
Sample Output
Cat:AAA,3
Dog:BBB,1
Tiger:CCC,2
#include <iostream>
using namespace std;
#include <string>
class Animal
{
public:
 string name;
 int age;
 virtual void Display() = 0;
};
class Cat :public Animal
{
public:
 Cat(){}
 Cat(string a, int b)
 {
  name = a;
  age = b;
 }
 void Display()
 {
  cout << "Cat:" << name << "," << age << endl;
 }
 ~Cat(){}
};
class Dog : public Animal
{
public:
 Dog(string a, int b)
 {
  name = a;
  age = b;
 }
 void Display()
 {
  cout << "Dog:" << name << "," << age << endl;
 }
 ~Dog(){}
};
class Tiger :public Cat
{
public:
 //Tiger(string a, int b):Cat(a, b){}  这种不用在父类中构造无参构造函数
 Tiger(string a, int b)//此法需要在Cat中构造无参构造函数
 {
  name = a;
  age = b;
 }
 void Display()
 {
  cout << "Tiger:" << name << "," << age << endl;
 }
 ~Tiger(){}
};
class Zoo//此类放在最后
{
public:
 int max;//最大动物数
 static int num;//动物数
 Animal** asp;//指针
 Zoo(int a)
 {
  max = a;
  asp = new Animal * [max];//动物数组 asp是指针
 }
 void Add(Animal* ap)//添加动物函数
 {
  if (num < max)
  {
   *(asp + num) = ap;
   num++;//计数
  }
 }
 void ShowAll()
 {
  for (int i = 0; i < num; i++)
  {
   asp[i]->Display();
  }
 }
 ~Zoo()
 {
  delete asp;//释放堆区创建的空间
 }
};
int Zoo::num = 0;
int main()
{
 Zoo z(5);
 Animal* pa = new Cat("AAA", 3);
 z.Add(pa);
 //delete pa;
 pa = new Dog("BBB", 1);
 z.Add(pa);
 //delete pa;
 pa = new Tiger("CCC", 2);
 z.Add(pa);
 //delete pa;
 z.ShowAll();
 return 0;
}
————————————————
版权声明:本文为CSDN博主「爱吃月饼的兔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dimrhyme/article/details/121961164

5.21 先设计一个基本账户类,再通过继承基本账户类设计一个储蓄账户类,储蓄账户类中增加一个静态成员变量(年利率),并增加如下成员函数

(1)计算月利息

月利息=存款金额 × 年利率 ÷12

(2)更改利率(静态方法):重新设置年利率

最后,编写一个程序来测试所设计的储蓄账户类能否实现预定的功能。

#include<iostream>
#include<string.h>
using namespace std;
class basic {
public:
 long id;
 basic(long a):id(a)      //基类构造函数
 {}
 void show(long id) {
  cout <<"账号:" << id << endl;
 }
 ~basic()        //基类析构函数
 {}
};
class account :private basic {
public:
 char name[20];
 long balance;
 char password[20];
 char address[20];
 long minimum_balance;
 float annual_interest_rate;
 account(char *a, long b, long c, char *d, char *e, long f, float g): //派生类构造函数
  basic(b), balance(c), minimum_balance(f), annual_interest_rate(g)
 {
  strcpy_s(name, a);
  strcpy_s(password, d);
  strcpy_s(address, e);
 }
 void show1();       //声明显示函数
 void deposit(long money);    //声明存款函数
 void withdrawal(long money);   //声明取款函数
 void change_password(char* chpassword); //声明修改密码函数
 void modify_name(char* chname);   //声明修改姓名函数
 void modification_address(char* chaddress);//声明修改地址函数
 ~account()        //派生类析构函数
 {}
};
 void account::show1()
 {
 cout << "姓名:" << name << endl;
 basic::show(id);
 cout << "余额:" << balance << endl;
 cout << "密码:" << password << endl;
 cout << "地址:" << address << endl;
 cout << "最小余额:" << minimum_balance << endl;
 cout << "年利率:" << annual_interest_rate << endl;
 }
 void account::deposit(long money) {
  balance += money;
 }
 void account::withdrawal(long money) {
  balance -= money;
 }
 void account::change_password(char *chpassword) {
  strcpy_s(password, chpassword);
 }
 void account::modify_name(char* chname) {
  strcpy_s(name, chname);
 }
 void account::modification_address(char* chaddress) {
  strcpy_s(address, chaddress);
 }
int main() {
 char name[20];
 long id;
 long balance;
 char password[20];
 char address[20];
 long minimum_balance;
 float annual_interest_rate;
 long money;      //存款或取款数额
 int a;
 cout << "请输入客户信息" << endl;
 cout << "---------------------------------" << endl;
 cout << "姓名:";
 cin >> name;
 cout << "账号:";
 cin >> id;
 cout << "余额:";
 cin >> balance;
 cout << "密码:";
 cin >> password;
 cout << "地址:";
 cin >> address;
 cout << "最小余额:";
 cin >> minimum_balance;
 cout << "年利率";
 cin >> annual_interest_rate;
 account n(name, id, balance, password, address, minimum_balance, annual_interest_rate);
 cout << "------------------------请输入操作------------------------------" << endl;
 cout << "1.打印客户信息" << endl << "2.存款" << endl << "3.取款" << endl;
 cout << "4.修改密码" << endl <<"5.修改姓名" << endl << "6.修改地址" << endl;
 while (scanf("%d", &a) != EOF) {
  switch (a) {
  case 1:
   n.show1();
   break;
  case 2:
   cout << "请输入存款金额:" << endl;
   cin >> money;
   n.deposit(money);
   cout << "当前余额:" << n.balance <<endl;
   break;
  case 3:
   cout << "请输入取款金额:" << endl;
   cin >> money;
   n.withdrawal(money);
   cout << "当前余额:" << n.balance << endl;
   break;
  case 4:
   char chpassword[20];
   cout << "请输入原始密码:" << endl;
   cin >> chpassword;
   if (strcmp(n.password, chpassword) != 0) {
    cout << "原始密码错误!请重新选择操作!" << endl;
   }
   else {
    cout << "请输入新密码:" << endl;
    cin >> chpassword;
    n.change_password(chpassword);
    cout << "修改成功!" << endl;
   }
   break;
  case 5:
   char chname[20];
   cout << "请输入修改后的姓名:" << endl;
   cin >> chname;
   n.modify_name(chname);
   cout << "修改成功!" << endl;
   break;
  case 6:
   char chaddress[20];
   cout << "请输入修改后的地址:" << endl;
   cin >> chaddress;
   n.modification_address(chaddress);
   cout << "修改成功!" << endl;
   break;
  }
 }
 return 0;
}

思考题

能否在储蓄账户类中增加一个功能:若是用户需要修改密码,需提供旧密码。

*继承 课后习题

5.18 设计一个点类,它仅包含两个属性:横坐标和纵坐标。通过继承点类再设计一个圆类,它除了有一个圆心外,还有半径,还应该能够计算圆的周长和面积等。编写一个测试程序来测试所设计的类能否实现预定的功能。
 5.19 设计一个动物类,它包含一些动物的属性,如名称、大小、重量等,动物可以跑或走。然后设计一个鸟类,除了动物的基本属性外,它还有自己的羽毛、翅膀等。鸟除了跑或走外,它还可以飞翔。为了继承动物类的特性,鸟类应该继承动物类。编写一个程序来测试所设计的鸟类能否实现预定的功能。
 5.20 先设计一个长方形类,再通过继承长方形类设计一个正方形类,正方形类中通过覆盖父类的成员函数得到一些新的功能。
  5.21 先设计一个基本账户类,再通过继承基本账户类设计一个储蓄账户类,储蓄账户类中增加一个静态成员变量(年利率),并增加如下成员函数:
  (1)计算月利息
  月利息=存款金额×年利率÷12
  (2)更改利率(静态方法):重新设置年利率
 最后,编写一个程序来测试所设计的储蓄账户类能否实现预定的功能。
 5.22先设计一个基本账户类,再通过继承基本账户类设计一个储蓄账户类,储蓄账户类中增加密码、地址、最小余额和利率等成员变量,并增加一些银行账户经常用到的成员函数。要求:
  (1)类中的成员函数具有输入、输出储户上述信息的功能;
  (2)将账号设计成不可更改;
  (3)修改密码时要提供原密码。
  5.23 在阅读5.6.1节内容的基础上,进一步分析图书馆的图书和杂志管理与借阅方式,设计一个基本符合图书馆实际工作方式的图书和杂志借阅系统。

上机四 运行时的多态性和抽象类程序设计

上机目的

1.理解多态性的概念。

2.掌握运行时的多态性概念和设计方法。

3.掌握虚函数的使用方法。

实验目的

理解多态性的概念。

掌握运行时的多态性概念和设计方法。

掌握虚函数的使用方法。

实验内容

P187:6.10,6.11

上机内容(p187 6.11)

编写计算正方体、圆柱体、球体的表面积和体积的类。要求:

(1) 这三个类有一个公共的基类;

(2) 这三个类计算正方体、圆柱体、球体的表面积和体积的成员函数名字相同;

(3) 按照运行时的多态性方法设计一个测试主函数,并进行测试。

正确源码

#include<iostream>
using namespace std;
class basic {
public:
 virtual double superficial_area() = 0;
 virtual double volume() = 0;
 ~basic()
 {}
};
class cube :public basic
{
private:
 double length;
public:
 cube(double a):length(a)
 {}
 double superficial_area()
 {
  return(6 * length * length);
 }
 double volume()
 {
  return(length * length * length);
 }
 ~cube()
 {}
};
class cylinder :public basic
{
private:
 double radius;
 double high;
public:
 cylinder(double a, double b):radius(a), high(b)
 {}
 double superficial_area()
 {
  return ((2 * 3.1415 * radius * radius) + (2 * 3.1415 * radius * high));
 }
 double volume()
 {
  return (3.1415 * radius * radius * high);
 }
 ~cylinder()
 {}
};
class globe :public basic
{
private:
 double radius;
public:
 globe(double a):radius(a)
 {}
 double superficial_area()
 {
  return (4 * 3.1415 * radius * radius);
 }
 double volume()
 {
  return (((3.1415 * 4) / 3) * radius * radius * radius);
 }
 ~globe()
 {}
};
int main() {
 int a;
 char c, e, f;
 double b, d, g, h;
 basic* p;
 cout << "请选择形状:" << endl;
 cout << "1.正方形" << endl << "2.圆柱体" << endl << "3.球体" << endl;
 cout << "-----------------------------------" << endl;
 while (scanf("%d", &a) != EOF) {
  switch (a) {
  case 1:
   {
   cout << "请输入边长:";
   cin >> b;
   cube cube1(b);
   p = &cube1;
   cout << "-----------------------------------" << endl;
   cout << "请选择操作:" << endl << "a.表面积" << endl << "b.体积" << endl << "c.返回" << endl;
   while (cin >> c) {
    if (c == 'a') {
     cout << "正方体的表面积是:" << p->superficial_area() << endl;
    }
    if (c == 'b') {
     cout << "正方体的体积是:" << p->volume() << endl;
    }
    if (c == 'c') {
     cout << "-----------------------------------" << endl;
     break;
    }
   }
   break;
  }
  case 2:
   {
   cout << "请输入半径:";
   cin >> d;
   cout << endl;
   cout << "请输入高:";
   cin >> h;
   cylinder cylinder1(d, h);
   p = &cylinder1;
   cout << "-----------------------------------" << endl;
   cout << "请选择操作:" << endl << "a.表面积" << endl << "b.体积" << endl << "c.返回" << endl;
   while (cin >> e) {
    if (e == 'a') {
     cout << "圆柱体的表面积是:" << p->superficial_area() << endl;
    }
    if (e == 'b') {
     cout << "圆柱体的体积是:" << p->volume() << endl;
    }
    if (e == 'c') {
     cout << "-----------------------------------" << endl;
     break;
    }
   }
   break;
   }
  case 3:
   {
   cout << "请输入半径:";
   cin >> g;
   globe globe1(g);
   p = &globe1;
   cout << "-----------------------------------" << endl;
   cout << endl;
   cout << "请选择操作:" << endl << "a.表面积" << endl << "b.体积" << endl << "c.返回" << endl;
   while (cin >> f) {
    if (f == 'a') {
     cout << "球体的表面积是:" << p->superficial_area() << endl;
    }
    if (f == 'b') {
     cout << "球体的体积是:" << p->volume() << endl;
    }
    if (f == 'c') {
     cout << "-----------------------------------" << endl;
     break;
    }
   }
   break;
  }
  }
 }
 return 0;
}
以下是一个示例实现,满足上述所有要求:
#include <iostream>
#include <cmath>
using namespace std;
// 定义基类Shape
class Shape {
public:
    // 虚析构函数
    virtual ~Shape() {}
    // 计算表面积的成员函数calcArea
    virtual double calcArea() = 0;
    // 计算体积的成员函数calcVolume
    virtual double calcVolume() = 0;
};
// 定义派生类Cube,继承自Shape
class Cube : public Shape {
private:
    double sideLength;
public:
    // 构造函数
    Cube(double sideLength) {
        this->sideLength = sideLength;
    }
    // 计算表面积
    double calcArea() override {
        return 6 * sideLength * sideLength;
    }
    // 计算体积
    double calcVolume() override {
        return sideLength * sideLength * sideLength;
    }
};
// 定义派生类Cylinder,继承自Shape
class Cylinder : public Shape {
private:
    double radius;
    double height;
public:
    // 构造函数
    Cylinder(double radius, double height) {
        this->radius = radius;
        this->height = height;
    }
    // 计算表面积
    double calcArea() override {
        return 2 * M_PI * radius * (radius + height);
    }
    // 计算体积
    double calcVolume() override {
        return M_PI * radius * radius * height;
    }
};
// 定义派生类Sphere,继承自Shape
class Sphere : public Shape {
private:
    double radius;
public:
    // 构造函数
    Sphere(double radius) {
        this->radius = radius;
    }
    // 计算表面积
    double calcArea() override {
        return 4 * M_PI * radius * radius;
    }
    // 计算体积
    double calcVolume() override {
        return (4.0 / 3.0) * M_PI * pow(radius, 3);
    }
};
// 测试主函数,使用指针数组和循环调用基类的成员函数
int main() {
    Shape* shapes[] = {
        new Cube(2.0),
        new Cylinder(1.0, 3.0),
        new Sphere(1.5)
    };
    int numShapes = sizeof(shapes) / sizeof(shapes[0]);
    for (int i = 0; i < numShapes; ++i) {
        cout << "Shape " << i+1 << ", Area: " << shapes[i]->calcArea()
             << ", Volume: " << shapes[i]->calcVolume() << endl;
        delete shapes[i];
    }
    return 0;
}
在上述代码中,所有形状都是从基类Shape继承而来,并且具有相同名称的calcArea()和calcVolume()成员函数。为了支持运行时的多态性(即派生类对象可以赋值给基类指针),这些成员函数均被声明为虚函数。
而在测试主函数中,我们创建了一个指针数组,存放了三个不同形状的对象。使用循环遍历每个对象,并分别调用基类的成员函数来计算表面积和体积。最后,为了释放动态分配的内存,我们还需要使用delete来删除每个对象。

实验用例所写的代码

#include<string>
#include<iostream>
using namespace std;
#define pi 3.1415926
class Shap
{
public:
virtual void area()=0;
virtual void volume()=0;
41
};
class Cube:public Shap//正方体
{
private:
float side_length;
public:
Cube(float Side_length):side_length(Side_length){}
void area()
{
cout<<"正方体表面积是:"<<6*pow(side_length,2)<<endl;
}
void volume()
{
cout<<"正方体体积是:"<<pow(side_length,3)<<endl;
}
};
class Cylinder:public Shap//圆柱体
{
private:
float r;
float h;
public:
Cylinder(float H,float R):r(R),h(H){}
void area()
{
cout<<"圆柱体表面积是:"<<2*pi*pow(r,2)+4*pi*r*h<<endl;
}
void volume()
{
cout<<"圆柱体体积是:"<<pi*pow(r,2)*h<<endl;
}
};
class Sphere:public Shap//球体
{
private:
float r;
public:
Sphere(float R):r(R){}
void area()
{
cout<<"球体表面积是:"<<4*pi*pow(r,2)<<endl;
}
void volume()
{
cout<<"球体体积是:"<<4*pi*pow(r,3)/3<<endl;
}
};
#include "stdafx.h"
#include<iostream>
#include<stdlib.h>
#include <stdio.h>
#include "class.h"
using namespace std;
void main(void)
{
Shap *p;
int n;//形状
char op;//操作
float side_length;
float r;
float h;
bool flag=1;
cout<<"请选择形状:"<<endl;
 cout<<"1:正方体"<<endl;
cout<<"2:圆柱体"<<endl;
cout<<"3:球体"<<endl;
// cin>>n;
loop: cout<<"-------------------------------------"<<endl;
while(cin>>n)
{
 switch(n)
 {
 case 1://正方体
44
 cout<<"请输入边长:";
 cin>>side_length;
 p=new Cube(side_length);
 cout<<"-------------------------------------"<<endl;
 cout<<"请选择操作:"<<endl;
 cout<<"a:表面积"<<endl;
 cout<<"b:体积"<<endl;
 cout<<"c:返回"<<endl;
 while(cin>>op)
 {
 switch(op)
 {
 case 'a':
 p->area();
 break;
 case 'b':
 p->volume();
 break;
 case 'c':
 goto loop;
 default:
 cerr<<"输入错误!"<<endl;
 }
 }
 break;
 case 2://圆柱体
 cout<<"请输入半径:";
 cin>>r;
 cout<<endl;
 cout<<"请输入高:";
 cin>>h;
 p=new Cylinder(r,h);
 cout<<"-------------------------------------"<<endl;
 cout<<"请选择操作:"<<endl;
 cout<<"a:表面积"<<endl;
 cout<<"b:体积"<<endl;
 cout<<"c:返回"<<endl;
 while(cin>>op)
 {
 switch(op)
 {
 case 'a':
 p->area();
 break;
 case 'b':
 p->volume();
 break;
 case 'c':
 goto loop;
default:
 cerr<<"输入错误!"<<endl;
 }
 }
 break;
 case 3://球体
 cout<<"请输入半径:";
 cin>>r;
 p=new Sphere(r);
 cout<<"-------------------------------------"<<endl;
 cout<<endl;
 cout<<"请选择操作:"<<endl;
 cout<<"a:表面积"<<endl;
 cout<<"b:体积"<<endl;
 cout<<"c:返回"<<endl;
 while(cin>>op)
 {
 switch(op)
 {
 case 'a':
 p->area();
 break;
 case 'b':
 p->volume();
 break;
 case 'c':
 goto loop;
 default:
 cerr<<"输入错误!"<<endl;
 }
 }
 break;
 }
}
}

p1876.10

程序阅读题

#include <iostream>
using namespace std;
class Base {//定义了一个Base类
 public:
  virtual void Set(int b) {//虚函数的使用
   x = b;
  }
  virtual int Get() {
   return x;
  }
 private://定义私有类
  int x;
};
class Derived: public Base {
 public:
  void Set(int d) {
   y = d;
  }
  int Get() {
   return y;
  }
 private:
  int y;
};
int main() {
 Base B_obj;
 Derived D_obj;
 Base *p = & B_obj;
 p->Set(100);
 cout << "B_obj x=" << p->Get() << endl;
 p = &D_obj;
 p->Set(200);
 cout << "D_obj x=" << p->Get() << endl;
 p->Base::Set(300);
 cout << "B_obj x=" << p->Base::Get() << endl;
 p->Set(p->Get() + 200);
 cout << "D_obj y=" << p->Get() << endl;
}

对这段代码的理解

这段代码是一个简单的继承示例,其中`Base`类作为基类,具有一个虚函数`Set()`和一个虚函数`Get()`,分别用于设置和获取私有成员变量`x`的值。
`Derived`类从`Base`派生,并覆盖了`Set()`和`Get()`函数,引入了新的私有成员变量`y`。由于这两个函数在`Base`中被声明为虚函数,则它们在`Derived`中也是虚函数。
在主函数中,首先创建了一个`Base`类型的对象`B_obj`和一个`Derived`类型的对象`D_obj`,并使用指向`Base`的指针`p`依次指向这两个对象。接着调用前者的`Set()`函数,并输出其值;然后调用后者的`Set()`函数,并输出其值。随后,又调用了基类中的`Set()`和`Get()`函数,并输出其结果。最后,在调用`p->Set(p->Get() + 200)`时,将使用`Derived`对象的`Set()`函数,为私有成员变量`y`重新赋值,并输出其结果。
需要注意的是,在这个例子中,如果想直接修改`Derived`对象的私有成员变量`y`,可以通过`Derived`类型的指针或引用实现。因此:

Derived d;

d.Set(100); // 修改 y 的值

Base* p = &d;

p->Set(100); // 修改 x 的值(Base 类中的私有成员变量)

是不同的效果,即通过指向基类的指针或引用修改派生类对象的私有成员变量,只能访问到其从基类继承得来的成员变量,不能直接访问派生类独有的成员变量。如果要修改派生类独有成员变量的值,需要使用指向派生类类型的指针或引用。
~~~`
此外,还需要注意以下几点:
1. `Base`中的`x`成员变量被设置为私有成员,因此只能通过`Set()`和`Get()`函数进行访问。在`Derived`类中并没有直接访问`Base`类的私有成员变量,而是引入了新的私有成员变量`y`,这样保证了数据的封装性。
2. 在`Derived`类中`Set()`和`Get()`函数使用了相同的名称,但它们与基类中同名函数不属于重载,而是函数覆盖(override)。在派生类中重新定义同名函数时,使用关键字`virtual`可以使其变为虚函数,并实现多态特性。当使用指向派生类对象的指针或引用调用这些函数时,将自动根据对象类型选择正确的函数版本。
3. 在基类和派生类的各个函数中,如果想避免出现隐藏的 Bug,应该使用作用域解析运算符(::)来明确指明要使用哪个类的函数或成员变量。比如,在最后一行的代码中,使用了如下语句:`p->Set(p->Get() + 200);`。由于`Derived`类中的成员变量和函数都被隐藏了,所以无法直接访问到`Derived`对象的私有成员变量`y`。需要使用`p->Get()`来获取当前对象的值,并利用`Set()`函数来重新赋值。在这个过程中,使用了作用域解析运算符来明确指明调用的是基类中的`Set()`和`Get()`函数。
4. 在这个例子中,将基类的指针或引用指向派生类对象时,使用了向上转型(upcasting)的方式。向上转型发生在从派生类类型到基类类型的隐式转换中,这是一种安全的转换方式。可以将指向派生类的指针或引用赋值给指向基类的指针或引用,从而可以通过基类接口操纵派生类对象。
5. 调用虚函数时,会依据调用者的实际类型选择正确的函数版本,即使用后期绑定(late binding)技术。在这个例子中,当`p`指向基类对象`B_obj`时,调用的是基类的`Set()`和`Get()`函数,输出了其私有成员变量`x`的值;当`p`指向派生类对象`D_obj`时,调用的是派生类的`Set()`和`Get()`函数,输出了其私有成员变量`y`的值。
6. 在最后一句代码中使用了链式操作语法:先调用`p->Get()`获取当前值,再将其加上 200,并作为参数传递给`p->Set()`函数进行修改。需要注意的是,由于`Set()`函数没有返回值,因此不能在一个语句中同时对其进行两次调用。如果想避免出错,可以分别对其进行多条语句的连续调用,或者将操作和输出分开进行。
总之,这个例子展示了基类和派生类继承关系中的一些基本语法和概念。在实际编程中,应该合理运用继承、多态等特性,遵循良好的设计原则和编码规范,以提高代码质量和可维护性。
除了基类和派生类之间的继承关系,C++还支持多重继承、虚继承、抽象类等高级特性。下面简单介绍一下这些概念:
1. 多重继承:一个派生类可以同时继承多个基类。该派生类将具有所有基类的成员和方法,并按照声明的顺序排列。如果多个基类具有相同的函数或成员变量,衍生类必须使用作用域解析运算符指定访问哪个基类的成员。
2. 虚继承:当一个派生类通过多层继承直接或间接地继承自同一基类时,可能会出现菱形继承的问题。为了避免这种情况,C++提供了虚继承的机制,使得派生类中只有一个共享基类的实例。
3. 抽象类:在 C++中,抽象类通常是指包含纯虚函数(即没有实现的虚函数)的类。抽象类不能被实例化,只能被用作基类来派生新的类。
除此之外,C++还支持运算符重载、类型转换、模板等高级特性,这些功能可以充分发挥 C++语言的优势,提高开发效率和代码质量。
在实际应用中,面向对象的编程思想可以帮助我们更好地组织程序结构,降低代码的复杂度和耦合度,提高代码的可扩展性和可重用性。下面列举一些使用面向对象编程思想的例子:
1. 游戏开发:游戏中的各种角色、场景、物品等都可以抽象成对象,通过继承和多态来实现不同类别之间的关系。
2. GUI 界面编程:GUI 控件是很常见的面向对象应用,例如按钮、文本框、菜单栏等都可以设计为独立的类,然后利用继承和虚函数来实现其共性和特性。
3. 数据库管理系统编程:数据库里的表、字段、记录等也可以用对象来表示,便于进行封装、抽象和组合。
4. 设计模式应用:设计模式是固定模式化的解决问题的方案,在某些复杂场景中尤其有用。例如工厂模式、单例模式、观察者模式等都是以对象为基础思想的典型例子。
总之,在 C++语言中,面向对象编程思想得到广泛应用,对软件设计与开发有着重要的贡献。但同时也需要注意遵循良好的设计、规范和命名等规则,才能使程序结构更清晰、简洁、易于理解和维护。
## 思考题
虚函数的运行机制是如何实现的,静态联编和动态联编有何区别?
虚函数是实现多态的关键机制,在 C++中,当一个基类指针或引用调用虚函数时,将自动进行后期绑定(late binding)或动态绑定(dynamic binding)动作,即在运行时根据实际对象类型动态解析调用哪个版本的虚函数。
那么虚函数的运行机制是如何实现的呢?在编译阶段,编译器会为每个包含虚函数的类生成一张虚函数表(vtable),其中保存了该类所有虚函数的地址。在运行阶段,通过访问对象的虚函数表中对应的位置,确定调用哪一个具体的虚函数。
静态联编和动态联编有什么区别呢?静态联编也称为早期绑定(early binding),是在编译阶段进行的函数调用绑定,即根据函数名和参数类型来识别调用哪个函数。这种绑定方式在效率上比较高,但不支持多态。
动态联编也称为后期绑定(late binding),则是在运行时进行的函数调用绑定,即根据实际对象类型确定调用哪个函数。这种绑定方式可以支持多态,使得程序更加灵活。但由于需要在运行时动态解析调用,因此效率相对较低。
在 C++中,默认情况下通过基类指针或引用进行函数调用时,是采用动态联编的方式。如果想要使用静态联编,需使用限定作用域运算符::(双冒号)来明确指明要调用的函数版本。
总之,虚函数实现了多态和动态绑定的特性,为 C++语言提供了强大的面向对象能力。掌握虚函数的运行机制和区别可以更好地理解和应用这一特性。
除了虚函数,C++还提供了纯虚函数(pure virtual function)和抽象类(abstract class)来实现接口和多态。
纯虚函数是指没有函数体的虚函数,其声明形式为 virtual type func() = 0;,其中 0 表示没有函数体。纯虚函数不能被直接调用,只能被子类重载实现。如果一个类包含纯虚函数,那么该类就成为抽象类,无法实例化。抽象类的作用是为了定义公共接口,用于派生新的类。
例如,下面的 Animal 类中定义了一个纯虚函数 eat(),并且因此成为了一个抽象类:
~~~cpp
class Animal{
    public:
        virtual void eat() = 0;
};

类似地,我们可以创建如下的 Dog、Cat 子类,并分别对 eat()纯虚函数进行重载:

class Dog : public Animal{
    public:
        void eat(){
            cout << "狗吃骨头" << endl;
        }
};
class Cat : public Animal{
    public:
        void eat(){
            cout << "猫吃鱼" << endl;
        }
};

这样,我们就可以通过基类指针或引用来调用子类对象的 eat()方法,并实现多态效果:

int main(){
    Animal *animal1 = new Dog();
    animal1->eat();  // 输出:“狗吃骨头”
    Animal *animal2 = new Cat();
    animal2->eat();  // 输出:“猫吃鱼”
    delete animal1;
    delete animal2;
    return 0;
}

总之,C++中的纯虚函数和抽象类为接口和多态的实现提供了重要支持,使得程序设计更加灵活和可扩展。在实际应用中,我们可以根据需要定义自己的接口和抽象类,以达到规范化、复用性和扩展性的目的。

除了虚函数和纯虚函数之外,C++中还有虚析构函数和多重继承等特性。

虚析构函数的作用是帮助确保在通过基类指针或引用销毁对象时能够正确调用子类析构函数。如果基类析构函数不是虚函数,那么当通过基类指针或引用销毁对象时,只会调用该对象的基类部分的析构函数。而如果基类析构函数是虚函数,那么当销毁子类对象时,会先调用子类的析构函数,然后再调用父类的析构函数。因此,在设计需要通过基类指针或引用销毁对象的程序时,应该将基类的析构函数定义为虚函数。

例如,下面是一个包含虚析构函数的基类 Shape,以及派生类 Rectangle 和 Triangle 的定义:

class Shape{
    public:
        virtual ~Shape(){}
};
class Rectangle : public Shape{
    public:
        ~Rectangle(){
            cout << "销毁矩形" << endl;
        }
};
class Triangle : public Shape{
    public:
        ~Triangle(){
            cout << "销毁三角形" << endl;
        }
};

在主函数中,我们可以使用基类指针来指向不同的子类对象,并尝试销毁这些对象:

int main(){
    Shape *shape1 = new Rectangle();
    Shape *shape2 = new Triangle();
    delete shape1;  // 输出:“销毁矩形”
    delete shape2;  // 输出:“销毁三角形”
    return 0;
}

多重继承是指一个派生类可以同时继承自两个或以上的基类。由于多重继承可能导致命名冲突和二义性等问题,在使用时需要特别注意设计。

例如,假设我们有形状 Shape、颜色 Color 和图案 Pattern 三个基类,现在需要定义一个具有形状、颜色和图案属性的新类 Graphic。那么我们可以像下面这样使用多重继承:

class Color{
    public:
        void setColor(string c){
            color = c;
        }
        string getColor() const{
            return color;
        }
    private:
        string color;
};
class Pattern{
    public:
        void setPattern(string p){
            pattern = p;
        }
        string getPattern() const{
            return pattern;
        }
    private:
        string pattern;
};
class Shape{
    public:
        virtual double area() const = 0;
        virtual void print() const = 0;
};
class Graphic : public Shape, public Color, public Pattern{
    public:
        void print() const{
            cout << "形状:" << endl;
            Shape::print();
            cout << "颜色:" << getColor() << endl;
            cout << "图案:" << getPattern() << endl;
        }
};

在 Graphic 类中,我们既继承了 Shape 的 area()和 print()接口,又继承了 Color 和 Pattern 的属性。在 print()方法中,我们还可以通过 getColor()和 getPattern()方法获取对象的颜色和图案等属性。

总之,C++中的虚析构函数和多重继承为程序设计提供了更多的灵活性和可扩展性。虚析构函数可以确保通过基类指针或引用销毁对象时能够正确调用子类析构函数;多重继承可以让派生类同时继承多个基类的接口和属性。在使用时,需要特别注意继承顺序、二义性和其他相关问题。

上机五:模板和异常处理程序设计(2 学时)

实验目的

1.了解参数多态性的概念。

2.熟悉函数模板和类模析的设计和使用方法。

3.掌握 C++异常处理方法和异常类的设计。

了解参数多态性的概念。

熟练掌握函数模板和类模析的设计和使用方法。

实验内容

P197:7.9

设计一个考虑异常情况的分数类模板,实现基本分数运算和分数的输出,并编写测试程序进行测试。

测试例题

[例 7.2] 设计一下复数类模板。要求:

(1)复数类模板的成员函数包括加和输出;

(2)成员函数加既可以是两个复数类模板对象相加,也可以是一个复数类模板对象与另一个模板参数作为实部的数值相加;

(3)设计一个测试主函数,要求测试主函数中同时定义实参为 float 的复数类对象和实参为 double 的复数类对象。

// TemplateComplex.h
template <class T>
class Complex{
 T real;
 T imag;
public:
 Complex(T x=0, T y=0);
 Complex Add(const Complex x);
 Complex Add(const T x);
void show() const;
};
template <class T>
Complex<T>::Complex(T x, T y)
{
real = x;
imag = y;
}
template <class T>
Complex<T> Complex<T>::Add(const Complex<T> x)
{
return Complex<T>(real+x.real, imag+x.imag);
}
template <class T>
Complex<T> Complex<T>::Add(const T x)
{
return Complex<T>(real+x, imag);
}
template <class T>
void Complex<T>::show() const
{
cout<<"real is "<<real;
cout<<" imag is "<<imag<<endl;
}
在主文件中 work5.cpp 中编辑测试代码:
#include <iostream.h>
#include "TemplateComplex.h"
int main(int argc, char* argv[])
{
Complex<float> x(1.1, 1.1), y(2.2, 2.2), z;
Complex<double> u(1.111111, 1.111111), v(2.22222, 2.22222), w;
z = x.Add(y);
cout<<"z is: ";
z.show();
w = u.Add(v);
cout<<"w is: ";
w.show();
return 0;
}
[例 7.3] 设计一个求两个数中较大数的函数模板
//Max.h
template <class T>
T max(T x, T y)
{
 return (x>y)? x:y;
}
在主文件中 work5.cpp 中编辑测试代码:
#include <iostream.h>
#include "Max.h"
int main(int argc, char* argv[])
{
int myIntX = 5, myIntY = 8, myIntC;
float myFloatX = 5.6, myFloatY = 8.9, myFloatC;
54
myIntC = max(myIntX, myIntY);
cout<<"myIntC = "<<myIntC<<endl;
myFloatC = max(myFloatX, myFloatY);
cout<<"myFloatC = "<<myFloatC<<endl;
}
//SeqStack.h
class PushOnFull{ //入栈满异常类
 int value;
public:
 PushOnFull(int x) : value(x){}
 int Value(void){ return value; }
};
class PopOnEmpty{};
class SeqStack{
int *data;
int MaxStackSize;
int top;
public:
SeqStack(int n)
{
top = 0;
MaxStackSize = 0;
data = new int[n];
}
~SeqStack(){ delete data; }
void Push(const int item);
int Pop();
};
void SeqStack::Push(const int item)
{
 if(top==MaxStackSize)
 throw PushOnFull(item);
 data[top] = item;
 top++;
}
int SeqStack::Pop()
{
if(top == 0) throw PopOnEmpty();
top--;
return data[top];
}
在主文件中 work5.cpp 中编辑测试代码:
#include <iostream.h>
#include "SeqStack.h"
int main(int argc, char* argv[])
{
SeqStack myStack(10);
try{
for(int i=0; i<11; i++)
myStack.Push(i+1);
}
catch(PushOnFull obj){
cout<<"Stack is full"<<endl;
cout<<"the element is: "<<obj.Value()<<endl;
}
try{
for(int i=0; i<11; i++)
cout<<myStack.Pop()<<" ";
}
catch(PopOnEmpty){
cout<<"Stack is empty"<<endl;
}
}

P197:7.9

设计一个考虑异常情况的分数类模板,实现基本分数运算和分数的输出,并编写测试程序进行测试。

class Fract{
public:
int n; //分子
int m; //分母
 Fract(int a=0, int b=1):n(a), m(b){}
 Fract Add(const Fract x);
Fract Div(const Fract x);
void show() const;
int MaxComm(int a, int b);
};
Fract Fract::Div(const Fract x)
{
57
58
int a = n*x.m;
int b = m*x.n;
if(b == 0)throw 0;
int c = MaxComm(a, b);
return Fract(a/c, b/c);
}
Fract Fract::Add(const Fract x)
{
int a = n*x.m + m*x.n;
int b = m*x.m;
int c = MaxComm(a, b);
return Fract(a/c, b/c);
}
int Fract::MaxComm(int a, int b)//求 a,b 的最大公约数
{
int r=a%b;
while(r!=0)
{
a = b;
b = r;
r = a%b;
}
return(b);
}
void Fract::show() const
{
cout<<n<<"/"<<m<<endl;
}
(3)在主文件中 work5.cpp 中编辑测试代码:
#include <iostream.h>
#include "Fract.h"
int main(int argc, char* argv[])
{
Fract f1(3, 4), f2(7, 16), f3;
try{
59
f1.Div(f3);
}
catch(int){
cout<<"Divided by Zero!"<<endl;
}
f3 = f1.Add(f2);
f3.show();
}

CHATgpt 会怎么来处理?

好的,下面是一个考虑异常情况的分数类模板,并实现了基本分数运算和分数输出的代码:
~~~c++
#include<iostream>
#include<exception>
using namespace std;
//自定义除数为0异常
struct DIVBYZERO: public exception
{
    const char* what() const throw()
    {
        return "Error: Division by zero!\n";
    }
};
template<class T>
class Fraction
{
    private:
        T numerator; // 分子
        T denominator; // 分母
    public:
        // 构造函数
        Fraction(T num, T den);
        // getter 方法
        T getNumerator();
        T getDenominator();
        // setter 方法
        void setNumerator(T num);
        void setDenominator(T den);
        // 重载"+"
        Fraction operator+(Fraction f);
        // 重载"-"
        Fraction operator-(Fraction f);
        // 重载"*"
        Fraction operator*(Fraction f);
        // 重载"/"
        Fraction operator/(Fraction f);
        // 输出分数
        void print();
};
// 构造函数实现
template<class T>
Fraction<T>::Fraction(T num, T den)
{
    if(den == 0) // 如果分母为0则抛出DIVBYZERO异常
        throw DIVBYZERO();
    numerator = num;
    denominator = den;
}
// getter 方法实现
template<class T>
T Fraction<T>::getNumerator()
{
    return numerator;
}
template<class T>
T Fraction<T>::getDenominator()
{
    return denominator;
}
// setter 方法实现
template<class T>
void Fraction<T>::setNumerator(T num)
{
    numerator = num;
}
template<class T>
void Fraction<T>::setDenominator(T den)
{
    // 如果分母为0则抛出DIVBYZERO异常
    if(den == 0)
        throw DIVBYZERO();
    else
        denominator = den;
}
// 重载"+" 实现
template<class T>
Fraction<T> Fraction<T>::operator+(Fraction f)
{
    T num = numerator * f.denominator + f.numerator * denominator;
    T den = denominator * f.denominator;
    return Fraction(num, den);
}
// 重载"-" 实现
template<class T>
Fraction<T> Fraction<T>::operator-(Fraction f)
{
    T num = numerator * f.denominator - f.numerator * denominator;
    T den = denominator * f.denominator;
    return Fraction(num, den);
}
// 重载"*" 实现
template<class T>
Fraction<T> Fraction<T>::operator*(Fraction f)
{
    T num = numerator * f.numerator;
    T den = denominator * f.denominator;
    return Fraction(num, den);
}
// 重载"/" 实现
template<class T>
Fraction<T> Fraction<T>::operator/(Fraction f)
{
    // 如果除数为0则抛出DIVBYZERO异常
    if(f.numerator == 0)
        throw DIVBYZERO();
    T num = numerator * f.denominator;
    T den = denominator * f.numerator;
    return Fraction(num, den);
}
//输出分数
template<class T>
void Fraction<T>::print()
{
    cout << " " << numerator << "/" << denominator << " ";
}
//测试
int main()
{
    try
    {
        Fraction<int> f1(3,8), f2(1,4);
        Fraction<int> f3 = f1 + f2;
        Fraction<int> f4 = f1 - f2;
        Fraction<int> f5 = f1 * f2;
        Fraction<int> f6 = f1 / f2;
        cout << "f1:"; f1.print(); cout << endl;
        cout << "f2:"; f2.print(); cout << endl;
        cout << "f1+f2:"; f3.print(); cout << endl;
        cout << "f1-f2:"; f4.print(); cout << endl;
        cout << "f1*f2:"; f5.print(); cout << endl;
        cout << "f1/f2:"; f6.print(); cout << endl;
        Fraction<double> f7(3.2, 1.5), f8(0, 4);
        Fraction<double> f9 = f7 + f8;
    }
    catch(DIVBYZERO &e)
    {
        cout << e.what() <<endl;
    }
    return 0;

代码上注释已经比较详细了,主要实现了以下功能:

  1. 自定义除数为 0 异常(DIVBYZERO)。
  2. 分别用构造函数和 setter 方法对分子和分母进行初始化,并提供 getter 方法获取成员变量值。
  3. 实现基本分数运算符重载( + , - , * , / )。
  4. 实现分数的输出功能。

在测试程序中,我们创建了两个不同数据类型的分数类(int 型和 double 型),并对其+、-、*、/四种运算符进行了使用。同时,也考虑到了除数为 0 时抛出错误处理。

上机六输入输出流程序设计

一、上机目的

1.了解 C++流类库结构。

2.掌握流类库中常用的类及其成员函数的使用方法。

3.了解常用控制符的使用,掌握 C++格式输入输出方法。

4.熟练掌握文件操作方法和文件流的输入输出。

二、上机内容

1.测试本章例题。

2.设计一个学生类,将 N 个学生对象的基本信息(姓名、身高、年龄)以二进制流的形式写入文件中,并将写入的文件信息在屏幕上进行随机显示

测试例题

[例 9.9] 随机访问文件。要求:

(1)设计一个包括姓名、学号和成绩的学生结构体。

(2)先把 3 个学生数据写入文件 bbb.dat,再从文件中读出第 2 个学生的数

据显示在屏幕上。

上机步骤:

(1)新建复数类模板头文件。选择菜单“File”——“New”,打开新建对话框,

如图 5-1 所示。选择“Files”标签中的“C/C++ Header File”,设置存储路径为

“D:\mywork\work6”,文件名称为“Student”。默认选中“Add to project”,如图 6-4

所示。

(2)单击“OK”按钮,打开 Student.h 文件,编辑代码如下:
//Student.h
struct Student{
char name[20];
long number;
int score;
};
(3)在主文件中 work6.cpp 中编辑测试代码:
#include "Student.h"
#include <fstream.h>
int main(int argc, char* argv[])
{
Student stu[3] = {{"Zhang", 111, 100}, {"Li", 222, 90}, {"Wang", 333, 80}}, 
stu2;
char filename[] = "bbb.data";
int n=3, i;
fstream datafile(filename, ios::in|ios::out|ios::binary);
for(i=0; i<3; i++)
datafile.write((char*)&stu[i], sizeof(struct Student));
datafile.seekg(1*sizeof(struct Student), ios::beg);
datafile.read((char*)&stu2, sizeof(struct Student));
datafile.close();
64
cout<<"Name: "<<stu2.name<<endl;
cout<<"Number: "<<stu2.number<<endl;
cout<<"score: "<<stu2.score<<endl;
return 0;
}

(4)在工具栏中单击“Build”命令图标 ,生成 work6.exe。

(5)在工具栏中单击“Excute”命令图标 ,运行 work6.exe,

  1. 上机实践
    设计一个学生类,将 N 个学生对象的基本信息(姓名、身高、年龄)以二进制流的形式写入文件中,并将写入的文件信息在屏幕上进行随机显示。

上机步骤:

(1)新建复数类模板头文件。选择菜单“File”——“New”,打开新建对话框,如图 6-6 所示。选择“Files””标签中的“C/C++ Header File”,设置存储路径为“D:\mywork\work6”,文件名称“Student2”。默认选中“Add to project”,如图 6-6所示。

图 6-6 新建学生类头文件

(2)单击“OK”按钮,打开 Student2.h 文件,编辑代码如下:
//Student2.h
#include <string.h>
class Student{
public:
char name[20];
float height;
int age;
Student(char *n=" ", float h=1.6, int a=20) : height(h), age(a)
{
strcpy(name, n);
}
};
(3)在主文件中 work6.cpp 中编辑测试代码:
#include "stdafx.h"
#include "Student2.h"
#include <fstream.h>
int main(int argc, char* argv[])
{
int n;
cout<<"please input the number of student: ";
cin>>n;
66
Student *ps, stu;
ps = new Student[n];
char name[20];
float height;
int age;
for(int i=0; i<n; i++)
{
cout<<endl<<"Name: "; cin>>name; 
cout<<"Height: "; cin>>height;
cout<<"Age: "; cin>>age; 
*(ps+i) = Student(name, height, age);
}
char filename[] = "info.data";
fstream datafile(filename, ios::in|ios::out|ios::binary);
for(i=0; i<n; i++)
datafile.write((char*)(ps+i), sizeof(struct Student));
int num;
cout<<"Please input the number to print: ";
cin>>num;
while(num)
{
datafile.seekg((num-1)*sizeof(struct Student), ios::beg);
datafile.read((char*)&stu, sizeof(struct Student));
cout<<"Name: "<<stu.name<<endl;
cout<<"Height: "<<stu.height<<endl;
cout<<"Age: "<<stu.age<<endl;
cout<<"Please input the number to print: ";
cin>>num;
}
datafile.close();
return 0;
}

(4)在工具栏中单击“Build”命令图标 ,生成 work6.exe。

(5)在工具栏中单击“Excute”命令图标 ,运行 work6.exe

思考题

1.在 C++中通过控制文件指针的移动实现文件的随机访问,一般文本文件难准确定位,所以随机访问多用于二进制文件,请测试。C++实现文件的随机访问需要考虑哪几个因素?

在 C++ 中实现文件的随机访问需要考虑以下几个因素:

  1. 打开文件时,需要指定正确的文件访问模式和打开方式。对于二进制文件,应该使用“二进制”模式来进行读写操作,以避免对数据的意外改变。
  2. 文件指针位置的移动。通过调用 fseek 函数,可以将文件指针移动到所需位置,从而实现随机访问。在移动指针时,需要注意偏移量的单位及正负方向,以确保文件指针移动到正确的位置。
  3. 读取或写入数据的大小和格式。对于二进制文件,由于数据类型的大小和格式可能不同,因此需要根据具体情况决定读取或写入的数据大小和格式。
  4. 关闭文件时,需要确保所有的文件指针都正确地指向了文件末尾,以避免数据的丢失和损坏。

下面是一个简单的示例,演示如何在C++中随机访问一个二进制文件:

#include <iostream>
#include <fstream>
using namespace std;
int main() {
    // 打开二进制文件
    fstream file("test.bin", ios::out | ios::binary);
    // 写入数据
    int num1 = 12345;
    float num2 = 3.1415926;
    file.write(reinterpret_cast<char*>(&num1), sizeof(num1));
    file.write(reinterpret_cast<char*>(&num2), sizeof(num2));
    // 将文件指针移动到开头
    file.seekg(0);
    // 读取数据
    int num3;
    float num4;
    file.read(reinterpret_cast<char*>(&num3), sizeof(num3));
    file.read(reinterpret_cast<char*>(&num4), sizeof(num4));
    // 输出数据
    cout << "num1 = " << num3 << endl;
    cout << "num2 = " << num4 << endl;
    // 关闭文件
    file.close();
    
    return 0;
}

在上面的示例中,我们首先打开了一个名为“test.bin”的二进制文件,并向其中写入了一个整型数和一个浮点数。接着,通过调用 seekg 函数将文件指针移动到文件开头处,然后读取了这两个数据并输出。最后关闭了文件。

2.测试 seekp 函数和 seekg 函数实现文件指针重定位的区别?

在 C++ 中,文件指针有两种类型:读取指针和写入指针。为了方便操作,C++ 标准库分别提供了 seekg 和 seekp 函数来实现对这两种指针位置的重定位。

seekg 函数是用于改变当前文件读指针位置的函数,它可以让文件读指针在文件中任意一个位置定位,从而可以实现读取数据时的随机访问。而 seekp 函数则是用于改变当前文件写指针位置的函数,它可以让文件写指针在文件中任意一个位置定位,从而可以实现插入和修改数据时的随机访问。

下面是一个简单的示例,演示如何使用 seekg 和 seekp 函数实现文件指针的重定位:

#include <iostream>
#include <fstream>
using namespace std;
int main() {
    // 打开文件
    fstream file("test.txt", ios::out | ios::in);
    // 写入数据
    file << "0123456789";
    // 重定位读指针(从开头开始向右移动3个字符)
    file.seekg(3, ios::beg);
    // 读取一个字符
    char ch;
    file.get(ch);
    cout << ch << endl;   // 输出3
    // 重定位写指针(从当前位置开始向右移动2个字符)
    file.seekp(2, ios::cur);
    // 插入一个字符
    file.put('A');
    // 重定位读指针(从开头开始向右移动2个字符)
    file.seekg(2, ios::beg);
    // 读取一个字符
    file.get(ch);
    cout << ch << endl;   // 输出0
    // 关闭文件
    file.close();
    
    return 0;
}

在上面的示例中,我们首先打开了一个名为“test.txt”的文件,并向其中写入了一串数字字符串。接着,通过调用 seekg 函数将文件读指针移动到该字符串的第四个字符位置处,然后读取了这个字符并输出。接下来,我们通过调用 seekp 函数将文件写指针移动到当前读指针的后两个字符位置处,然后向文件中插入了一个大写字母“A”。最后,我们再次通过调用 seekg 函数将文件读指针移动到该字符串的第三个字符位置处,然后再次读取了这个字符并输出。

可以看出,通过使用 seekg 和 seekp 函数,我们可以灵活地控制文件指针的位置,从而实现了对文件数据的灵活访问和修改。需要注意的是,两个函数的偏移量参数的计算方式以及参数的含义略有不同,请根据具体情况选择合适的参数类型和参数值。

3.补充完善[上机实践]题目内容,对 N 个学生信息,根据用户需求(按照身高、年龄)对学生信息进行排序后再显示。

上机的一次作业

上机作业:

编写一个基本帐户类。成员变量包括:帐号、储户姓名和存款余额,成员函数包括:存款和取款。编写一个测试程序对该类功能进行测试。

要求:上传源码及运行结果截图。

分析思路:

针对题目要求,C++实现了一个基本的帐户类(Account)。该类包含三个成员变量(account_no、account_name和balance)和三个成员函数(deposit、withdraw和display)。其中:

  • 构造函数(Account)用于初始化帐号信息(account_no, account_name和balance);
  • 存款函数(deposit)用于向当前余额(balance)中添加一定金额(amount);
  • 取款函数(withdraw)用于从当前余额(balance)中扣除一定金额(amount),如果取款金额大于当前余额则给出提示"Insufficient balance";
  • 显示函数(display)用于显示当前帐户的账号(account_no)、储户姓名(account_name)和余额(balance)。

测试程序中,我首先创建一个帐户对象(account)并输出其初始余额,然后进行存款、取款操作,并观察操作后的余额变化情况。最后展示更新后的所有帐户信息。

在C++实现中,我使用了字符串(string)类型作为帐号和储户姓名的数据类型

好的,我来更加详细地解释一下。此代码是C++语言写的一个简单的银行账户管理系统。

首先定义了一个名为 Account 的类,表示银行账户,其中 Account 类有两个构造函数:有参构造函数和无参构造函数。这样就可以在不同条件下创建不同的银行账户对象,并提供默认的初始化参数值,非常灵活。

接着,核心在于该类提供了存款、取款、获取余额三个方法。其中,存款方法 deposit() 只需要输入要存入金额即可,而取款方法 withdraw() 需要对输入取款金额判断当前账户的余额是否小于取款金额,从而决定是否允许取款,并返回一个标识是否允许取款的结果;余额查询方法 getBalance() 则只需直接返回当前账户余额即可。

最后是主函数 main() ,主要流程如下:

  1. 用户通过标准输入 cin 输入账户户名,帐号和初始存款金额;
  2. 使用用户输入的信息,创建了一个账户对象;
  3. 输出菜单栏供用户选择进行存款、取款或者余额查询操作;
  4. 根据用户选择和输入的金额执行对应的存款、取款或查询操作;
  5. 循环执行 3 - 4 步骤,直到用户退出;

整个程序的结构清晰,设计合理,运用了 C++ 中的类、构造函数和成员函数等面向对象的特性,有很好的可读性和扩展性。

小学版

可以这样解释给小学生听:

这段代码是一个模拟银行账户的程序,就好像你有一个自己的钱罐一样。它能够帮助你记录你存了多少钱,取了多少钱,还有你现在有多少钱了。

里面有一个叫做 Account 的东西,它就像是一个专门用来处理钱罐的工具。它帮你记录你的账号、姓名和余额等信息,而且它还能帮你进行存款、取款和查询余额等操作。

主人公是一个叫做 main() 的人,他就像是一个坐在你身边的管理者,帮你实现各种对钱罐的操作。当你要存钱时,你只需要告诉他存入多少,他就会帮你把钱放进去;当你要取钱时,你要先告诉他你要取多少钱,他会先检查你有没有这么多钱,如果你的钱罐里没有那么多,就不能取;当你想知道你现在有多少钱时,你只需要问他,他会告诉你。

整个程序很有条理,每个部分都有自己的任务和职责,让你方便地管理你的钱罐(账户),避免花钱时不知道自己还有多少钱的尴尬情况。

中学版

可以这样给中学生解释:

这段代码是用C++编程语言写的一个小程序。它模拟了银行账户的创建、存款、取款和查询余额等操作。你可以把它看作是一个非常简单的银行账户管理系统。

在程序中,Account 类定义了类构造函数和成员函数来实现存款、取款和查询余额等操作。其中,类的有参构造函数接收用户输入的账户姓名、账号和初次存款金额等信息,并完成账户对象的创建;类的成员函数比较重要的包括存款方法 deposit()、取款方法 withdraw() 和余额查询方法 getBalance() ,它们分别对用户输入的存款金额、取款金额进行判断、执行存取款操作或返回当前账户的余额。

主函数 main() 是整个程序执行的控制中心,负责输出菜单栏并接受用户的选择,循环执行相应的操作直到用户退出。

这段代码运用了 C++ 中面向对象的编程思想,如类、构造函数和成员函数等概念,并通过 switch 循环来判断用户输入的菜单栏选择,实现程序的基本逻辑。同时,代码非常规范,注释详细,易于理解,展现了良好的编码习惯和风格。

给废物大学生如何解释呢

作为 AI 助手,我不鼓励使用“废物大学生”这样的说法,我们应该相互尊重。下面是一个更为中性和客观的解释。

这段代码是一个用 C++ 编程语言写的小程序,主要实现了银行账户的创建和管理系统。它中心类是 Account,可以完成开户、存款、取款和查询余额等功能。

在编程中,将这些操作封装成类的成员函数,使得每个函数仅关注自己的职责,整体结构更加清晰易懂。其中,有参构造函数通过接收用户输入的参数来初始化一个新的账户对象;存款方法使用加法运算符来实现存款功能;取款方法使用判断语句进行异常处理,从而保证账户余额充足时才能够提款;查询余额方法直接返回成员变量值即可。

主函数 main() 作为整个程序运行的控制中心,对用户输入进行处理,并且输出菜单提示,根据用户选择的菜单选项调用相应的成员函数,执行对应的功能。

在程序设计过程中,变量命名规范,代码风格一致,采用了详细的注释和英文字符界面,方便修改和调试。总之,这个程序展现出良好的代码组织结构和流程控制逻辑。

给专业开发者如何解释呢

基于您是专业开发者这一前提,我可以给您详细地解释一下:

这段代码是一个基于 C++ 编程语言开发的银行账户管理系统,主要使用了面向对象编程的思想。其中定义了一个名为 Account 的类,该类包含了一个无参构造函数和一个有参构造函数,以及三个成员函数,分别实现存款、取款及余额查询等操作。

类的构造函数主要完成对象的初始化工作,有参构造函数接受用户输入的参数来为对象的属性进行赋值工作;存款方法通过参数更新当前账户总金额(mBalance);取款方法首先检测是否可以修改 mBalance 再进行取款操作,并返回一个标记钱量是否足够的标志;余额查询方法直接返回当前账户的总金额。

在主函数中,用户可以通过标准输入 cin 来创建自己的银行账户,并且根据菜单提示选择不同的操作功能,在此过程中使用 switch 循环结构来接收菜单选项并调用相应的类成员函数,从而完成操作。

在编写此程序时,我们采用了合理的代码组织结构,变量命名规范,注释详尽,界面友好,这些都大大提高了代码的可读性和可扩展性,并遵循了相关的编码规范。在程序运行过程中,可以有效地支持账户的管理和查询等基本功能。

给婴儿怎么解释(胎教),编程要从娃娃抓起

对于婴儿来说,他们还是一个正在学习世界、认知事物的过程,所以我们可以简单地形容一下。

这个程序就像是一个懂得计算的小机器人,它能够帮你记录你存了多少钱,取了多少钱,还有你现在有多少钱了。有点像你听故事时,会有不同的角色用不同的声音为你讲述整个故事。

通过这个小机器人,你可以学习到关于钱的概念,可以理解存钱和取钱的方式,并且可以灵活地选择自己需要的功能。比如,你可以通过输入信息开启一个新的账户,当你存款或取款时,你只需要告诉这个小机器人前来大门口(输入界面),然后跟着它的指示操作即可完成你的需求。同时,在进行操作时它也会给你一定的提示,帮助你更好地理解整个过程。

总之,这个小机器人可以帮助你认识并理解一些基本的金融知识。等你长大后,如果你想深入学习计算机编程,这将是一个很好的开始!

给可爱的猫猫怎么解释呢~~~

好的,那我们来用猫咪可以理解的方式分享一下:

小猫咪,你看看这是一个有趣的程序呢!它可以帮助我们管理银行账户哦。就像当你在灵活地使用行动系统时,让你的食物、书籍和玩具方便地组织在一起,也不会弄混了它们在哪里一样。

里面有很多叫做函数和类的东西,就看作我们小时候玩的积木,它们可以帮我们搭建出一个模拟银行账户的世界。主人公 main() 呢,就像是个小管家,可以通过菜单栏来控制整个系统,让我们进行存款、取款或查询余额等操作。而 Account 类则是这个世界的核心机构,负责处理我们的账号、姓名、余额和交易等信息,全方位地服务于我们的使用需求。

总之,这个程序非常有趣并且实用,帮助我们更好的管理金钱,方便我们从紊乱中获得一个清晰的账户视图,从而使我们在生活中能够更好地掌握资金的流向,以达到理财规划的目的,让我们的生活变得更加美好快乐。

代码展示

#include <iostream>
#include <cstring >
using namespace std;
class Account {
  public:
    Account(char name[], long num, float amount);   //类的有参构造函数
    Account();                    //类的无参构造函数
    void deposit(float amount);     //往当前账户中存款
    int withdraw(float amount);     //从当前账户中取款
    float getBalance();         //查询当前账户的余额
  private:
    char mName[20];       //银行账户的户名
    long mSN;           //本账户的帐号
    float mBalance;       //本账户当前的余额
};
//类的有参构造函数
Account::Account(char name[], long num, float amount) {
  strcpy(mName, name);      //字符串复制函数
  mSN = num;
  mBalance = amount;
}
//类的无参构造函数
Account::Account() {
  cout << "无参函数被调用!" << endl;
}
//往当前账户中存款
void Account::deposit(float amount) {
  mBalance = mBalance + amount;
}
//从当前账户中取款
int Account::withdraw(float amount) {
  if (amount > mBalance) {
    return 0;
  } else if (amount <= mBalance) {
    mBalance = mBalance - amount;
    return 1;              //return 1代表函数非正常终止
  }
}
//查询当前账户的余额
float Account::getBalance() {
  return mBalance;
}
//主函数
int main() {
  int NO, m;
  char name[20];
  long num;
  float amount;
  cout << "请输入所开账户户名:";
  cin >> name;
  cout << "请输入所开账户帐号:";
  cin >> num;
  cout << "请输入所开账户初始存款金额:";
  cin >> amount;
  Account A(name, num, amount);
  cout << "" << endl;
  cout << "------------------------------------------------" << endl;
  cout << "                   菜单栏                       " << endl;
  cout << "1、存款请输入“1”" << endl;
  cout << "" << endl;
  cout << "2、取款请输入“2”" << endl;
  cout << "" << endl;
  cout << "3、查询账户余额请输入“3”" << endl;
  cout << "" << endl;
  cout << "4、退出请输入“4”" << endl;
  cout << "" << endl;
  cout << "------------------------------------------------" << endl;
  while (1) {
    cout << "请输入选择:" << endl;
    cin >> NO;
    switch (NO) {       //通过switch循环来判断输入的菜单栏选择对应其相应的操作
      case 1:
        cout << "请输入存款金额:";
        cin >> amount;
        A.deposit(amount);
        break;                //表示跳出该switch语句体
      case 2:
        cout << "请输入取款金额:";
        cin >> amount;
        m = A.withdraw(amount);
        if (m == 0)
          cout << "当前账户余额不足!" << endl;
        else
          cout << "取款成功!" << endl;
        break;
      case 3:
        cout << "当前账户余额为:" << A.getBalance() << endl;
        break;
      case 4:
        cout << "账户已退出!" << endl;
        return 0;
      default:
        cout << "输入错误!" << endl;      //判断输入菜单栏是否输入正确
        exit(0);
    }
    cout << "" << endl;
  }
}

实验:小型通讯录管理程序设计(4学时)

实验目的

深入理解面向对象技术的封装性、继承性和多态性,掌握面向对象程序设计方法。

熟悉基于对话框的MFC应用程序创建过程,熟悉ClassWizard工具的使用方法。

掌握常用控件和C++文件流类的使用方法。

这是一个非常实用的程序设计实验,可以让学生深入理解面向对象的三大特性:封装、继承和多态,并且熟悉基于对话框的MFC应用程序创建过程。具体的实验内容包括:

  1. 设计一个通讯录类Contact,其中包括联系人姓名、电话号码和电子邮箱等信息,同时还需要编写新增、删除、修改和查询联系人的成员函数。
  2. 使用链表来管理通讯录中的联系人,通过链表操作来实现增、删、改、查功能。
  3. 使用文件来存储通讯录数据,利用C++文件流类中的文件读写操作函数,将通讯录数据写入到文件中,并从文件中读取数据。
  4. 使用MFC框架创建通讯录管理程序,包含如下控件:编辑框、列表框、按钮等。并且利用ClassWizard工具自动生成通讯录管理程序相关代码。
  5. 最后进行程序测试,对所有功能进行测试验证,确保程序能够正确地实现通讯录的增、删、改、查和保存等各项功能。

总之,本次实验旨在让学生熟悉面向对象程序设计方法,加深对C++文件流类和MFC框架的掌握,提高编程水平和实战经验。

实验内容

设计一个小型通讯录管理程序,实现通讯录的新建、保存、以及通讯录的增、删、改、查功能。(提示:通过链表进行通讯录各种操作,并使用文件存储通讯录。由于CString对象具有不定长的特点,在使用文件流操作文件时不应使用该类型。)

以下是一个基于MFC框架的通讯录管理程序设计示例,实现了通讯录的新建、保存、以及通讯录的增、删、改、查功能。其中使用了链表来管理通讯录中的联系人,通过文件读写操作将通讯录数据写入到文件中,并从文件中读取数据。
```
// Contact.h文件
#pragma once
#include <string>
using namespace std;
class CContact
{
public:
    string name;
    string phoneNumber;
    string email;
    CContact* next;
};
class CContactList
{
public:
    CContactList(void);
    virtual ~CContactList(void);
public:
    void saveToFile(const char* fileName);
    void loadFromFile(const char* fileName);
    void addContact(CContact& contact);
    void deleteContact(CContact* prev, CContact* current);
    bool modifyContact(CContact* contact, const string& name, const string& phoneNumber, const string& email);
    CContact* findContact(const string& name);
private:
    CContact* m_pHead;
};
```
```
// Contact.cpp文件
#include "stdafx.h"
#include "Contact.h"
#include <fstream>
#include <iostream>
CContactList::CContactList(void)
{
    m_pHead = new CContact;
    m_pHead->next = NULL;
    loadFromFile("contact.dat");
}
CContactList::~CContactList(void)
{
    saveToFile("contact.dat");
    if (m_pHead)
    {
        CContact* p = m_pHead;
        while (p)
        {
            CContact* q = p;
            p = p->next;
            delete q;
        }
        m_pHead = NULL;
    }
}
void CContactList::saveToFile(const char* fileName)
{
    ofstream ofs(fileName, ios::binary);
    if (!ofs) return;
    CContact* p = m_pHead->next;
    while (p)
    {
        int name_len = p->name.length();
        ofs.write((char*)&name_len, sizeof(name_len));
        ofs.write(p->name.c_str(), name_len);
        int phone_len = p->phoneNumber.length();
        ofs.write((char*)&phone_len, sizeof(phone_len));
        ofs.write(p->phoneNumber.c_str(), phone_len);
        int email_len = p->email.length();
        ofs.write((char*)&email_len, sizeof(email_len));
        ofs.write(p->email.c_str(), email_len);
        p = p->next;
    }
    ofs.close();
}
void CContactList::loadFromFile(const char* fileName)
{
    ifstream ifs(fileName, ios::binary);
    if (!ifs) return;
    CContact* p = m_pHead;
    while (ifs.peek() != EOF)
    {
        CContact* q = new CContact;
        int name_len;
        ifs.read((char*)&name_len, sizeof(name_len));
        char* name_buf = new char[name_len+1];
        ifs.read(name_buf, name_len);
        name_buf[name_len] = '\0';
        q->name = name_buf;
        delete [] name_buf;
        int phone_len;
        ifs.read((char*)&phone_len, sizeof(phone_len));
        char* phone_buf = new char[phone_len+1];
        ifs.read(phone_buf, phone_len);
        phone_buf[phone_len] = '\0';
        q->phoneNumber = phone_buf;
        delete [] phone_buf;
        int email_len;
        ifs.read((char*)&email_len, sizeof(email_len));
        char* email_buf = new char[email_len+1];
        ifs.read(email_buf, email_len);
        email_buf[email_len] = '\0';
        q->email = email_buf;
        delete [] email_buf;
        p->next = q;
        q->next = NULL;
        p = q;
    }
    ifs.close();
}
void CContactList::addContact(CContact& contact)
{
    CContact* p = m_pHead;
    while (p->next)
    {
        p = p->next;
    }
    CContact* q = new CContact;
    q->name = contact.name;
    q->phoneNumber = contact.phoneNumber;
    q->email = contact.email;
    q->next = NULL;
    p->next = q;
}
void CContactList::deleteContact(CContact* prev, CContact* current)
{
    if (!current || !prev) return;
    prev->next = current->next;
    delete current;
}
bool CContactList::modifyContact(CContact* contact, const string& name, const string& phoneNumber, const string& email)
{
    if (!contact) return false;
    contact->name = name;
    contact->phoneNumber = phoneNumber; 
    contact->email = email;
    return true;
}
CContact* CContactList::findContact(const string& name)
{
    CContact* p = m_pHead->next;
    while (p)
    {
        if (p->name == name)
        {
            return p;
        }
        p = p->next;
    }
    return NULL;
}
```
```
// MyCommDlg.h文件
#pragma once
#include "Contact.h"
class CMyCommDlg : public CDialogEx
{
public:
    CMyCommDlg(CWnd* pParent = nullptr);    // 标准构造函数
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_MYCOMM_DIALOG };
#endif
    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
protected:
    HICON m_hIcon;
    vector<CString> m_columnNames;
    CRichEditCtrl m_richEditCtrl;
    CContactList m_contactList;
protected:
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnBnClickedButtonNewcontact();
    afx_msg void OnBnClickedButtonSave();
    afx_msg void OnBnClickedButtonModify();
    afx_msg void OnBnClickedButtonDelete();
    afx_msg void OnBnClickedButtonFind();
};
```
```
// MyCommDlg.cpp文件
#include "stdafx.h"
#include "MyComm.h"
#include "MyCommDlg.h"
#include "afxdialogex.h"
#include <sstream>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CMyCommDlg::CMyCommDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_MY


目录
相关文章
|
5月前
|
算法 开发工具 计算机视觉
【零代码研发】OpenCV实验大师工作流引擎C++ SDK演示
【零代码研发】OpenCV实验大师工作流引擎C++ SDK演示
76 1
|
5月前
|
存储 搜索推荐 C++
C++课程设计实验杭州电子科技大学ACM题目(中)
C++课程设计实验杭州电子科技大学ACM题目(中)
33 1
|
6月前
|
C++
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
|
5月前
|
存储 JavaScript 前端开发
程序与技术分享:C++程序设计实验考试准备资料(2019级秋学期)
程序与技术分享:C++程序设计实验考试准备资料(2019级秋学期)
|
6月前
|
Serverless C++ 容器
【期末不挂科-C++考前速过系列P5】大二C++实验作业-多态性(3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P5】大二C++实验作业-多态性(3道代码题)【解析,注释】
|
5月前
|
存储 人工智能 测试技术
C++课程设计实验杭州电子科技大学ACM题目(下)
C++课程设计实验杭州电子科技大学ACM题目(下)
32 0
|
5月前
|
存储 C++
C++课程设计实验杭州电子科技大学ACM题目(上)
C++课程设计实验杭州电子科技大学ACM题目(上)
32 0
|
6月前
|
C++ 芯片
【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】
|
21天前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
21 4