一、计划
1.进入网址 启程网络 - 新简约软件开发工作室 往下滑找到下图;
2.下载题库,可以选择上面网站的,也可以去未来教育(没有广告tb/pxx)
二、练习
1.函数重写(覆盖)
//语句①②哪个是有二义性
class B1 {
public:
void fun1(){}
void fun2(int){}
};
class B2 {
public:
void fun1(){}
void fun2(){}
};
class D : public B1, public B2 {//声明了一个名为 D 的类,它是从 B1 和 B2 两个基类派生而来的。public 表示采用公有继承方式,这意味着基类的 public 成员在派生类中仍然是 public 的。
public:
void fun1(){} //定义了一个名为 fun1 的成员函数,返回值为 void,不接受任何参数,函数体为空。这个函数会覆盖从基类 B1 和 B2 继承来的 fun1 函数。
};
int main() {
D obj;
obj.fun1(); //①调用 obj 对象的 fun1 函数。由于 D 类中重写了 fun1 函数,所以这里调用的是 D 类自己定义的 fun1 函数。
obj.fun2(10); //②这行代码会引发编译错误。因为 D 类从 B1 和 B2 继承了两个不同版本的 fun2 函数,一个接受 int 类型参数(来自 B1),另一个不接受参数(来自 B2),编译器无法确定调用哪个版本的 fun2 函数,产生了二义性。
return 0;
}
class B1 {
public:
int val;
void fun1() {
std::cout << "B1::fun1 called" << std::endl;
}
};
class B2 {
public:
void fun1() {
std::cout << "B2::fun1 called" << std::endl;
}
private:
int val;
};
class D : public B1, public B2 {
public:
void fun1() {
std::cout << "D::fun1 called" << std::endl;
}
};
int main() {
D obj;
obj.val = 1;//有二义性,编译器无法确定你要访问的是 B1 类中的 val 还是 B2 类中的 val,所以会产生二义性错误。虽然 B2 中的 val 是私有成员,不能直接访问,但从语法二义性角度有影响,可改为 obj.B1::val = 1; 用了作用域解析运算符 ::,明确告诉编译器,你要访问的是从 B1 类继承过来的 val 成员变量,这样就消除了二义性,让代码能够正确编译和访问到想要的成员
obj.fun1(); // 类自己定义了 fun1 函数,在调用 obj.fun1() 时,优先调用自身定义的函数,不会产生二义性。
return 0;
}
2. 函数参数传递
#include <iostream> //引入输入输出流头文件,用于实现控制台的输入输出功能
using namespace std; //使用 std 命名空间,这样在后续代码中可以直接使用 cout 等标准库中的标识符,而不需要写 std:: 前缀
class Point
{
public: //访问修饰符后的内容可以在类外部被访问
Point() { x = y = 0; } //默认构造函数,将成员变量 x 和 y 初始化为 0
Point(int i, int j) { x = i; y = j; } //带参数的构造函数,用于初始化 x 和 y 为传入的参数值
void copy(Point &m); //函数声明,用于复制一个 Point 对象的坐标值
void setxy(int i, int j) { x = i; y = j; } //函数用于设置 Point 对象的坐标值
void print() { cout << x << "," << y << " "; } //在控制台输出 Point 对象的坐标
private:
int x, y; //这两个成员变量只能在类内部被访问和操作
};
void Point::copy(Point &m) //实现了 Point 类的 copy 函数,通过引用参数 m 来获取另一个 Point 对象的坐标,并将其赋值给自己的 x 和 y 成员变量。
{
x = m.x;
y = m.y;
}
void fun(Point &m1, Point m2) //定义了一个名为 fun 的函数,接受一个 Point 对象的引用 m1 和一个 Point 对象 m2
//在函数内部,通过调用 setxy 函数分别修改 m1 和 m2 的坐标。注意由于 m2 是值传递,对 m2 的修改不会影响到外部传入的实参。
{
m1.setxy(12, 15);
m2.setxy(22, 25);
}
int main()
{
Point p(5, 7), q; //创建了两个 Point 对象 p 和 q,p 初始坐标为 (5, 7),q 使用默认构造函数初始化。
q.copy(p); //调用 q.copy(p) 将 p 的坐标复制给 q。
fun(p, q); //调用 fun(p, q) 函数,该函数会修改 p 的坐标为 (12, 15),但 q 作为值传递的参数,其原始值不受函数内部修改的影响。
p.print();//输出结果是 12,15
q.print(); //输出结果是 5,7
return 0;
}
#include <iostream>
using namespace std;
class Point
{
public:
Point() { x = y = 0; }
Point(int i, int j) { x = i; y = j; }
void copy(Point *m);
void setxy(int i, int j) { x = i; y = j; }
void print() { cout << x << ',' << y << ','; }
private:
int x, y;
};
void Point::copy(Point *m)//定义了之前在类中声明的 copy 函数。这里 Point:: 表示该函数是 Point 类的成员函数。函数功能是将指针 m 所指向的 Point 对象的 x 和 y 坐标值分别赋给当前对象的 x 和 y 成员变量。
{
x = m->x;
y = m->y;
}
void fun(Point m1, Point *m2)//定义一个普通函数 fun,接受一个 Point 类的对象 m1 和一个指向 Point 类对象的指针 m2 作为参数
{
m1.setxy(12, 15);
m2->setxy(22, 25);
}
int main()
{
Point p(5, 7), q;
q.copy(&p);
fun(p, &q);//调用 fun 函数,将 p 按值传递,q 的地址传递给函数。在函数内部,p 的副本被修改,但 p 本身不受影响,而 q 的坐标被修改为 (22, 25)
p.print();//5 7
q.print();//22 25
return 0;
}
3.插入新分数,淘汰最小分数
FigureSkating.h
#pragma once //这是一个预处理指令,用于确保头文件只被编译一次,避免重复包含。
#pragma warning(disable:4996) //这是一个预处理指令,用于禁用编译器的特定警告信息,这里禁用的是警告编号为 4996 的警告。
const int NUM = 8;//定义了一个常量 NUM,其值为 8,用于表示数组的大小。
class CFigureSkating //定义了一个名为CFigureSkating的类(花样滑冰)
{
private:
double score[NUM];//类的私有成员变量,定义了一个包含 NUM 个元素的双精度浮点数数组 score,用于存储选手的得分。
public:
CFigureSkating(void);//类的默认构造函数声明,用于创建 CFigureSkating 类的对象,没有参数。
CFigureSkating(double []);//类的带参数构造函数声明,接受一个双精度浮点数数组作为参数,用于初始化 score 数组。
~CFigureSkating(void);//类的析构函数声明,用于在对象销毁时进行清理工作。
double QualifyingRound(double);//类的成员函数声明,接受一个双精度浮点数作为参数,返回一个双精度浮点数,表示被淘汰的分数。
void PrintScore();//类的成员函数声明,用于输出选手的得分。
};
main.cpp
#include "FigureSkating.h" //包含自定义的头文件 FigureSkating.h
#include <iostream> //#include <iostream> 包含标准输入输出流的头文件,以便使用 cin 和 cout 进行输入输出操作。
using namespace std; //这行代码使用了 std 命名空间,这样在使用 std 命名空间中的标识符(如 cout、cin 等)时就不需要显式地加上 std:: 前缀。
extern void writeToFile(const char *);//这行代码声明了一个外部函数 writeToFile,该函数接受一个 const char * 类型的参数,返回值为 void。extern 关键字表示这个函数是在其他文件中定义的。
int main()
{
double currentScore[NUM] = {96.5, 93.7, 85.4, 82.1, 74.3, 70.7, 65.4, 32.1}; //定义了一个包含 NUM 个元素的双精度浮点数数组 currentScore,并对其进行初始化。NUM 是在 FigureSkating.h 中定义的常量。
double newScore; //用于存储用户输入的新分数
CFigureSkating fs(currentScore); //创建了一个 CFigureSkating 类的对象 fs,并使用 currentScore 数组作为参数调用构造函数进行初始化。
cout << "花样滑冰晋级赛成绩单" << endl; //
cout << "目前各晋级分数是:";
fs.PrintScore();//调用 CFigureSkating 类的 PrintScore 成员函数,输出当前选手的得分
cout <<"-------------------------------------" << endl
<< "请输入当前选手得分:";
cin >> newScore;
while (newScore != -1)
{
cout << "现在被淘汰的分数是:";
cout << fs.QualifyingRound(newScore) << endl;//调用 CFigureSkating 类的 QualifyingRound 成员函数,传入用户输入的新得分,并输出该函数的返回值,即被淘汰的分数
cout << "最新各晋级分数是:";
fs.PrintScore();//调用 CFigureSkating 类的 PrintScore 成员函数,输出当前选手的得分。
cout << "-------------------------------------" << endl
<< "请输入当前选手得分:";
cin >> newScore;//从标准输入读取用户输入的新得分,并存储到 newScore 变量中。
}
writeToFile("");//调用外部函数 writeToFile,传入一个空字符串作为参数。
return 0;
}
FigureSkating.cpp
#include "FigureSkating.h"
#include <iostream>
using namespace std;
CFigureSkating::CFigureSkating(void)//CFigureSkating 类的默认构造函数定义。
{
for (int i = 0; i < NUM; i++) //构造函数的实现,将 score 数组的所有元素初始化为 0.0。
score[i] = 0.0;
}
CFigureSkating::CFigureSkating(double initScore[NUM])//CFigureSkating 类的带参数构造函数定义,接受一个双精度浮点数数组作为参数。
{
for (int i = 0; i < NUM; i++)//构造函数的实现,将参数数组 initScore 的元素依次赋值给 score 数组。
score[i] = initScore[i];
}
CFigureSkating::~CFigureSkating(void)//CFigureSkating 类的析构函数定义,这里为空实现,因为没有需要释放的资源。
{
}
double CFigureSkating::QualifyingRound(double newScore)//CFigureSkating 类的 QualifyingRound 成员函数定义,接受一个双精度浮点数作为参数。
{
int i, insertPosition = NUM;//定义了两个局部变量,insertPosition 用于记录新得分的插入位置,初始化为 NUM,
double outScore;//outScore 用于记录被淘汰的分数
for (i = 0; i < NUM; i++)//遍历 score 数组,找到新得分应该插入的位置,如果新得分大于某个元素,则将该位置记录到 insertPosition 中,并跳出循环
if (newScore > score[i])
{
insertPosition = i;
break;
}
//********333********
if (insertPosition != NUM)//如果插入的位置不是数组的大小,即就是可以插进数组里面,则直接
{
outScore = score [NUM-1];//先将数组最后一个元素,也是最小的元素给淘汰掉
for (i = NUM - 1; i > insertPosition; i--)//从数组最后一个元素往前遍历
score [i] = score [i - 1];//将前一个的元素赋给后一个元素
score [insertPosition] = newScore;//将新的分数再插入到要插入的位置
}
else//否则,要插入的位置是数组的大小,那我们就直接将新拿到的分给淘汰掉
outScore = newScore;
//********666********
return outScore;//返回淘汰的分数
}
void CFigureSkating::PrintScore()//CFigureSkating 类的 PrintScore 成员函数定义。
{
for (int i = 0; i < NUM; i++)//遍历 score 数组,将数组中的元素依次输出,每个元素之间用制表符分隔,最后输出换行符
cout << score[i] << '\t';
cout << endl;
}
4.指针
#include<iostream>
using namespace std;
class Vehicle {
public:
void run() { cout << "vehicle run!" << endl; };
void stop() { cout << "vehicle stop!" << endl; };
};
class Motorcar : public Vehicle {
public:
void run() { cout << "motorcar run!" << endl; };
void stop() { cout << "motorcar stop!" << endl; };
};
int main() {
Motorcar m;
m.run();
Vehicle* vp = &m;//创建一个指向 Vehicle 类的指针 vp ,并让它指向 Motorcar 对象 m ,因为 Motorcar 是 Vehicle 的子类,存在类型兼容关系,可以这样赋值
vp->stop();//过指针 vp 调用 stop 函数,这里由于没有使用虚函数(C++ 中通过 virtual 关键字声明虚函数),所以调用的是指针类型(Vehicle 类)的 stop 函数,输出 "vehicle stop!" 。
return 0;
}
5.派生类构造函数、成员函数重写
#include <iostream>
// 包含输入输出流库,用于使用 cout 进行输出操作
#include <string>
// 包含字符串库,用于使用 string 类型
using namespace std;
// 使用标准命名空间,这样在使用标准库中的类和函数时可以省略 std:: 前缀
class Surname
{
// 定义一个名为 Surname 的类,用于表示姓氏
public:
Surname(string s) {
// 定义 Surname 类的构造函数,接受一个 string 类型的参数 s
SetSurname(s);
// 调用 SetSurname 方法,将传入的参数 s 设置为姓氏
}
void SetSurname(string s)
{
// 定义一个公共成员函数 SetSurname,用于设置姓氏
surname = s;
// 将传入的参数 s 赋值给类的保护成员变量 surname
}
void Print() const
{
// 定义一个公共的常量成员函数 Print,用于输出姓氏
cout << surname << " ";
// 使用 cout 输出姓氏,并在后面添加一个空格
}
protected:
string surname;
// 定义一个保护成员变量 surname,用于存储姓氏
};
class Name : public Surname
{
// 定义一个名为 Name 的类,它是 Surname 类的公有派生类,用于表示姓名
public:
Name(string s, string n) : Surname(s)
{
// 定义 Name 类的构造函数,接受两个 string 类型的参数 s 和 n
// 使用成员初始化列表调用基类 Surname 的构造函数,将 s 作为姓氏传递给基类
//Name 类继承自 Surname 类,意味着 Name 类包含了 Surname 类的特征,其中姓氏就是 Surname 类所管理的核心属性。当创建 Name 对象时,它需要初始化从 Surname 类继承来的姓氏部分。通过将 s 传递给基类 Surname 的构造函数,我们可以确保 Name 对象中的姓氏属性得到正确的初始化。
SetName(n);
// 调用 SetName 方法,将传入的参数 n 设置为名字
}
void SetName(string n)
{
// 定义一个公共成员函数 SetName,用于设置名字
name = n;
// 将传入的参数 n 赋值给类的私有成员变量 name
}
void Print() const
{
// 定义一个公共的常量成员函数 Print,用于输出完整的姓名
Surname::Print();
// 调用基类 Surname 的 Print 方法,输出姓氏
cout << name << "\n";
// 使用 cout 输出名字,并换行
}
private:
string name;
// 定义一个私有成员变量 name,用于存储名字
};
int main()
{
// 定义程序的入口函数 main
Name onePerson("zhang", "yu");
// 创建一个 Name 类的对象 onePerson,传入姓氏 "zhang" 和名字 "yu"
onePerson.Print();
// 调用 onePerson 对象的 Print 方法,输出完整的姓名
return 0;
// 程序正常结束,返回 0
}
6.判断一个数是否为两个阶乘之和的算法
#include<iostream>
#include<cmath>
#include <string>
using namespace std;
// 定义Factorial类,用于判断一个数是否为两个阶乘之和
class Factorial
{
public:
// 构造函数,接收一个整数target,将其赋值给成员变量max
Factorial(int target){max = target; }
// 判断是否是两个阶乘之和的成员函数
bool IsFactorialSum();
// 打印结果的成员函数,输出max = left! + right! 的形式
void Print(){
cout<<this->max<<"="<<this->left<<"!+"<<this->right<<"!"<<endl;
}
private:
int max; // 目标整数,即要判断是否为两个阶乘之和的数
int left,right; // 找到的两个阶乘数,满足 left! + right! = max
int factorialSet[11]; // 保存阶乘数的数组,最多保存10个阶乘值
};
// 声明一个将结果写入文件的函数,这里只是声明,未实现具体功能
void writeToFile(const string);
// 实现Factorial类的IsFactorialSum成员函数
bool Factorial::IsFactorialSum()
{
int i=1, n=1;
// 计算从1开始的各个阶乘,保存在数组factorialSet中
while (n<max)
{
factorialSet[i]=n;
n*=(++i);
}
// 双重循环遍历阶乘数组,寻找满足两个阶乘之和等于max的组合
for (int j = 1; j <= i; j++) {
for (int k = 1; k <= i; k++) {
if (factorialSet[j] + factorialSet[k] == max) {
left = j;
right = k;
return true; // 找到满足条件的组合,返回true
}
}
}
return false; // 未找到满足条件的组合,返回false
}
// 主函数,程序的入口点
int main()
{
int target;
cout<<"请输入一个整数,回车键结束:";
cin>>target; // 从标准输入读取一个整数,赋值给target
Factorial obj(target); // 创建Factorial类的对象obj,传入目标整数target
// 调用IsFactorialSum函数判断是否为两个阶乘之和
if (obj.IsFactorialSum())
obj.Print(); // 如果是,调用Print函数输出结果
else
cout<<"给定的数不是两个阶乘之和"<<endl; // 如果不是,输出提示信息
writeToFile(""); // 调用writeToFile函数,这里传入空字符串
return 0; // 程序正常结束,返回0
}
// 这里可以实现writeToFile函数,但目前只是占位
void writeToFile(const string) {
// 可以在这里添加将结果写入文件的具体代码
}
7.有疑惑
#include <iostream> // 引入输入输出流头文件,用于使用cin、cout等输入输出操作
using namespace std; // 使用标准命名空间,这样在使用标准库中的对象和函数时可以省略std::前缀
class Integer{ // 定义一个名为Integer的类
public: // 声明公有成员,外部可以访问这些成员
Integer(int val=0):value(val){} // 构造函数,带有默认参数0,用于初始化类中的value成员变量
Integer operator+(Integer itg) { // 对运算符+进行重载,使得Integer类的对象之间可以像内置类型(如int)一样使用+运算符进行加法运算。在这个函数中,它将当前对象(调用该运算符函数的对象)的value成员与传入对象itg的value成员相加,并返回相加的结果
//在实际编程中,如果有多个Integer类的对象需要进行加法操作,就可以直接使用+运算符。例如Integer num1(3), num2(5); Integer result = num1 + num2;,这里num1 + num2就会调用重载的operator+函数,实现两个Integer对象内部value值的相加,从而方便地完成类似普通整数相加的操作。
return value += itg.value; // 将当前对象的value成员与传入对象的value成员相加,并返回结果
}
friend ostream& operator<<(ostream& os,Integer& it){ // 声明一个友元函数,用于重载输出流运算符“<<”,当需要在控制台输出Integer类的对象时,无需编写复杂的输出代码,直接使用cout << integerObject;(integerObject为Integer类的对象)即可。比如在调试程序查看Integer对象的值,或者展示计算结果时,这种方式让输出操作更加简洁直观,就像输出普通的int类型变量一样。
return os << it.value; // 将对象的value成员输出到输出流os中,并返回该输出流
}
private: // 声明私有成员,只能在类内部访问
int value; // 类的私有成员变量,用于存储整数值
};
int main() { // 主函数,程序的入口点
Integer zero; // 创建一个Integer类的对象zero,调用默认构造函数,value初始化为0
Integer one; // 创建一个Integer类的对象one,调用默认构造函数,value初始化为0
one = zero + 1; // 这里会发生隐式类型转换,将1转换为Integer类型,然后调用重载的“+”运算符函数
// 计算zero.value + 1,并将结果赋值给one.value
cout << "zero=" << zero << ", "; // 输出字符串"zero=",然后调用重载的“<<”运算符函数输出zero对象的value值,再输出字符串", "
cout << "one=" << one << endl; // 输出字符串"one=",然后调用重载的“<<”运算符函数输出one对象的value值,再换行
return 0; // 程序正常结束,返回0给操作系统
}
8.指针(指针类型与成员访问权限:基类指针只能访问基类声明的成员,即使指向派生类对象)
#include <iostream> // 引入输入输出流头文件,用于输入输出操作(如 cout)
using namespace std; // 使用标准命名空间,允许直接使用 cout、endl 等标识符
class CBase { // 定义基类 CBase
protected:
int n; // 受保护成员变量 n
public:
CBase(int i) : n(i) {} // 构造函数,用参数 i 初始化成员 n
void Print() { // 成员函数 Print,输出基类的 n 值
cout << "CBase:n= " << n << endl;
}
};
class CDerived : public CBase { // CDerived 类公有继承 CBase
public:
int v; // 公有成员变量 v
CDerived(int i) : CBase(i), v(2 * i) {} // 构造函数,先调用基类构造函数,再初始化 v
void Func() {} // 成员函数 Func(无具体实现)
void Print() { // 重写基类的 Print 函数,输出派生类的 n 和 v
cout << "CDerived:n= " << n << endl;
cout << "CDerived:v= " << v << endl;
}
};
int main() {
CDerived objDerived(3); // 创建派生类对象 objDerived,传入参数 3
CBase objBase(5); // 创建基类对象 objBase,传入参数 5
CBase* pBase = &objDerived; // ① 基类指针指向派生类对象,合法操作
CDerived* pDerived; // 声明派生类指针 pDerived
pDerived = &objDerived; // 派生类指针指向派生类对象
cout << "使用派生类指针调用函数" << endl;
pDerived->Print(); // ② 派生类指针调用自身成员函数 Print,合法
cout << "使用基类指针调用函数" << endl;
pBase = pDerived; // ③ 基类指针指向派生类指针,合法
pBase->Print(); // 调用基类指针指向对象的 Print 函数(实际调用派生类重写版本)
pBase->Func(); // ④ 语法错误!CBase 类中没有 Func 成员函数,无法调用
cout << "使用派生类指针调用函数" << endl;
pDerived->Print(); // 派生类指针再次调用 Print 函数
return 0;
}
9.友元函数,实现 < 运算符重载,操作的是 Integer 对象
#include <iostream> // 引入输入输出流头文件,用于实现输入输出功能(如使用 cout 输出)
using namespace std; // 使用标准命名空间,避免每次使用标准库功能时都写 std::
class Integer { // 定义基类 Integer
public:
Integer(): n(0) {} // Integer 构造函数,初始化私有成员 n 为 0
// 声明友元函数(重载 < 运算符),允许该函数访问类的私有成员
friend bool operator<(const Integer &b1, const Integer &b2);
private:
int n; // 私有成员变量 n
};
class PosInteger: public Integer { // PosInteger 类公有继承 Integer
public:
PosInteger(): n(0) {} // PosInteger 构造函数,初始化自身私有成员 n 为 0
void setN(int i) { n = i; } // 设置自身私有成员 n 的值
private:
int n; // 自身私有成员变量 n
};
// 实现友元函数,重载 < 运算符
bool operator< (const Integer &b1, const Integer &b2) {
return b1.n < b2.n; // 比较两个 Integer 对象的私有成员 n
}
int main() { // 程序入口函数
PosInteger a1, a2; // 创建 PosInteger 类对象 a1 和 a2
a1.setN(5); // 设置 a1 自身的私有成员 n 为 5
a2.setN(10); // 设置 a2 自身的私有成员 n 为 10
// 比较 a1 和 a2:由于 operator< 操作的是基类 Integer 的成员 n(始终为 0),而非 PosInteger 自身的 n
cout << (a1 < a2) << endl;
return 0; // 程序正常结束,返回 0
}
10.
#include <iostream>
// 引入标准输入输出流库,用于使用 cin 和 cout 进行输入输出操作
#include <cmath>
// 引入数学库,使用其中的 max 和 min 函数
using namespace std;
// 使用标准命名空间,这样就可以直接使用标准库中的类和函数,而无需加 std:: 前缀
class CShape //基类:图形类
{
protected:
double perimeter; //图形的周长,子类可访问
// 定义一个受保护的成员变量 perimeter,用于存储图形的周长
public:
CShape()
{
};
// 基类的默认构造函数,目前为空,不做任何操作
virtual ~CShape()
{
};
// 基类的虚析构函数,使用 virtual 关键字是为了确保在通过基类指针删除派生类对象时,能正确调用派生类的析构函数
virtual double CalPeri()
{ return 0;
};
// 虚函数,用于计算图形的周长,基类中默认返回 0,派生类可以重写该函数实现具体计算
virtual void setPeri(double peri){};
// 虚函数,用于设置图形的周长,基类中为空,派生类可以重写该函数实现具体设置
virtual void PrintInfo(){};
// 虚函数,用于打印图形的信息,基类中为空,派生类可以重写该函数实现具体打印
};
class CRectangle : public CShape
{
double length, width;
// 定义矩形的长和宽
public:
CRectangle(double l, double w)
{
length = max(l,w);
width = min(l,w);
};
// 带参数的构造函数,初始化矩形的长和宽,确保长大于等于宽
CRectangle()
{
length = 0;
width = 0;
};
// 默认构造函数,将长和宽初始化为 0
~CRectangle()
{
};
// 析构函数,目前为空,不做任何操作
virtual double CalPeri();
// 声明虚函数,用于计算矩形的周长
virtual void setPeri(double);
// 声明虚函数,用于设置矩形的周长
virtual void PrintInfo();
// 声明虚函数,用于打印矩形的信息
};
double CRectangle::CalPeri()
{
//**********found**********
return 2*(length+width);
}
// 实现计算矩形周长的函数,根据矩形周长公式 2×(长 + 宽) 计算并返回结果
void CRectangle::PrintInfo()
{ cout<<"\t长度 = "<<this->length<<", 宽度 = "<<this->width<< ", 周长 = "<<this->perimeter<<endl;
}
// 实现打印矩形信息的函数,使用 this 指针访问当前对象的成员变量,输出矩形的长、宽和周长
void CRectangle::setPeri(double peri)
{
//**********found**********
perimeter=peri;
}
// 实现设置矩形周长的函数,将传入的参数赋值给成员变量 perimeter
CShape *pShapes[100]; //用来存放矩形,最多100个
// 定义一个基类指针数组,最多可以存储 100 个指向 CShape 类型对象的指针,实际用于存储 CRectangle 对象
int main()
{ int i, n;
double temp1, temp2;
CRectangle *pr;
// 定义循环变量 i 和矩形个数 n,临时变量 temp1 和 temp2 用于存储输入的长和宽,以及一个指向 CRectangle 对象的指针 pr
cout<<"请输入矩形的个数:";
cin>>n;
// 提示用户输入矩形的个数,并将输入的值存储到变量 n 中
if (n<=0) return 0;
// 如果输入的矩形个数小于等于 0,程序直接结束
cout<<"请分别输入各矩形的两条边的长度,以空格为分隔符,回车键结束:";
for( i = 0; i < n; ++i )
{
cin>>temp1>>temp2;
// 提示用户输入每个矩形的两条边的长度,并将输入的值存储到 temp1 和 temp2 中
pr = new CRectangle(temp1, temp2);
// 使用 new 运算符动态创建一个 CRectangle 对象,并将其地址赋值给指针 pr
pr->setPeri(pr->CalPeri());
// 调用 CRectangle 对象的 CalPeri 函数计算周长,并将结果通过 setPeri 函数设置到对象的 perimeter 成员变量中
//**********found**********
pShapes[i]=pr;
}
// 将创建的 CRectangle 对象的指针存储到基类指针数组 pShapes 中
cout<<"共有"<<n<<"个矩形,如下:"<<endl;
for ( i = 0; i < n; ++i )
{ cout<<"第"<<i+1<<"个矩形:";
//**********found**********
pShapes[i]->PrintInfo();
// 通过基类指针调用 PrintInfo 函数,由于 PrintInfo 是虚函数,会根据实际指向的对象类型调用 CRectangle 类的 PrintInfo 函数
delete pShapes[i]; //释放空间
// 使用 delete 运算符释放动态分配的内存,防止内存泄漏
}
return 0;
}