当前位置: 首页 > news >正文

OOP-实验2

实验任务1

源代码T.h,T.cpp,task1.cpp

点击查看代码 T.h
#pragma once#include <string>// 类T: 声明
class T
{// 对象属性、方法
public:T(int x = 0, int y = 0); // 普通构造函数T(const T &t);           // 复制构造函数T(T &&t);                // 移动构造函数~T();                    // 析构函数void adjust(int ratio); // 按系数成倍调整数据void display() const;   // 以(m1, m2)形式显示T类对象信息private:int m1, m2;// 类属性、方法
public:static int get_cnt(); // 显示当前T类对象总数public:static const std::string doc; // 类T的描述信息static const int max_cnt;     // 类T对象上限private:static int cnt; // 当前T类对象数目// 类T友元函数声明friend void func();
};// 普通函数声明
void func();
点击查看代码 T.cpp
#include "T.h"
#include <iostream>
#include <string>// 类T实现// static成员数据类外初始化
const std::string T::doc{"a simple class sample"};
const int T::max_cnt = 999;
int T::cnt = 0;// 类方法
int T::get_cnt()
{return cnt;
}// 对象方法
T::T(int x, int y) : m1{x}, m2{y}
{++cnt;std::cout << "T constructor called.\n";
}T::T(const T &t) : m1{t.m1}, m2{t.m2}
{++cnt;std::cout << "T copy constructor called.\n";
}T::T(T &&t) : m1{t.m1}, m2{t.m2}
{++cnt;std::cout << "T move constructor called.\n";
}T::~T()
{--cnt;std::cout << "T destructor called.\n";
}void T::adjust(int ratio)
{m1 *= ratio;m2 *= ratio;
}void T::display() const
{std::cout << "(" << m1 << ", " << m2 << ")";
}// 普通函数实现
void func()
{T t5(42);t5.m2 = 2049;std::cout << "t5 = ";t5.display();std::cout << '\n';
}
点击查看代码 task1.cpp
#include "T.h"
#include <iostream>void test_T();int main()
{std::cout << "test Class T: \n";test_T();std::cout << "\ntest friend func: \n";func();
}void test_T()
{using std::cout;using std::endl;cout << "T info: " << T::doc << endl;cout << "T objects'max count: " << T::max_cnt << endl;cout << "T objects'current count: " << T::get_cnt() << endl<< endl;T t1;cout << "t1 = ";t1.display();cout << endl;T t2(3, 4);cout << "t2 = ";t2.display();cout << endl;T t3(t2);t3.adjust(2);cout << "t3 = ";t3.display();cout << endl;T t4(std::move(t2));cout << "t4 = ";t4.display();cout << endl;cout << "test: T objects'current count: " << T::get_cnt() << endl;
}

运行测试截图

屏幕截图 2025-10-22 092316

  • 问题1:T.h中,在类T内部,已声明func是T的友元函数。在类外部,去掉line36,重新编译,程序能否正常运行。如果能,回答YES;如果不能,以截图形式提供编译报错信息,说明原因。

  • 回答:编译报错信息见下图,产生原因是func声明在类T内部,在task1.cpp中调用func时,找不到func的声明。在VSCode上使用C++11标准,为func添加类型为T的参数,则可以正常运行,据此猜测编译器可以通过func的参数,寻找到类T中func的声明。

屏幕截图 2025-10-22 102239

  • 问题2:T.h中,line9-12给出了各种构造函数、析构函数。总结它们各自的功能、调用时机。

  • 回答:line9的函数为普通构造函数,功能是以传入的参数x,y构造类T的对象,在传入0或1或2个int类型的参数时调用;line10的函数是复制构造函数,功能是创建一个和t一样的对象,在需要拷贝对象t时调用;line11的函数是移动构造函数,功能是创建一个新的对象管理t的资源,在不需要t或减少资源开销时调用;line12的函数是析构函数,功能是销毁对象,释放资源,在对象的生命周期结束后自动调用。

  • 问题3:T.cpp中,line13-15,剪切到T.h的末尾,重新编译,程序能否正确编译。如不能,以截图形式给出报错信息,分析原因。

  • 回答:可以正确编译。

实验任务2

源代码Complex.h,Complex.cpp,task2.cpp

点击查看代码 Complex.h
#pragma once#include <string>class Complex
{
public:Complex(double _real = 0, double _imag = 0);Complex(const Complex &other);double get_real() const;double get_imag() const;void add(const Complex &other);friend void output(const Complex &c);friend double abs(const Complex &c);friend Complex add(const Complex &c1, const Complex &c2);friend bool is_equal(const Complex &c1, const Complex &c2);friend bool is_not_equal(const Complex &c1, const Complex &c2);private:double real, imag;public:static const std::string doc;
};void output(const Complex &c);
double abs(const Complex &c);
Complex add(const Complex &c1, const Complex &c2);
bool is_equal(const Complex &c1, const Complex &c2);
bool is_not_equal(const Complex &c1, const Complex &c2);
点击查看代码 Complex.cpp
#include <iostream>
#include <cmath>
#include "Complex.h"const std::string Complex::doc = "a simplified complex class";Complex::Complex(double _real, double _imag) : real{_real}, imag{_imag} {}Complex::Complex(const Complex &other) : real{other.real}, imag{other.imag} {}double Complex::get_real() const
{return real;
}double Complex::get_imag() const
{return imag;
}void Complex::add(const Complex &other)
{real += other.real;imag += other.imag;
}void output(const Complex &c)
{if (c.imag >= 0)std::cout << c.real << " + " << c.imag << "i";elsestd::cout << c.real << " - " << -c.imag << "i";
}double abs(const Complex &c)
{return std::sqrt(c.real * c.real + c.imag * c.imag);
}Complex add(const Complex &c1, const Complex &c2)
{return Complex(c1.real + c2.real, c1.imag + c2.imag);
}bool is_equal(const Complex &c1, const Complex &c2)
{return (c1.real == c2.real) && (c1.imag == c2.imag);
}bool is_not_equal(const Complex &c1, const Complex &c2)
{return !is_equal(c1, c2);
}
点击查看代码 task2.cpp
// 待补足头文件
#include "Complex.h"
#include <iostream>
#include <iomanip>
#include <complex>void test_Complex();
void test_std_complex();int main()
{std::cout << "*******测试1: 自定义类Complex*******\n";test_Complex();std::cout << "\n*******测试2: 标准库模板类complex*******\n";test_std_complex();
}void test_Complex()
{using std::boolalpha;using std::cout;using std::endl;cout << "类成员测试: " << endl;cout << Complex::doc << endl<< endl;cout << "Complex对象测试: " << endl;Complex c1;Complex c2(3, -4);Complex c3(c2);Complex c4 = c2;const Complex c5(3.5);cout << "c1 = ";output(c1);cout << endl;cout << "c2 = ";output(c2);cout << endl;cout << "c3 = ";output(c3);cout << endl;cout << "c4 = ";output(c4);cout << endl;cout << "c5.real = " << c5.get_real()<< ", c5.imag = " << c5.get_imag() << endl<< endl;cout << "复数运算测试: " << endl;cout << "abs(c2) = " << abs(c2) << endl;c1.add(c2);cout << "c1 += c2, c1 = ";output(c1);cout << endl;cout << boolalpha;cout << "c1 == c2 : " << is_equal(c1, c2) << endl;cout << "c1 != c2 : " << is_not_equal(c1, c2) << endl;c4 = add(c2, c3);cout << "c4 = c2 + c3, c4 = ";output(c4);cout << endl;
}void test_std_complex()
{using std::boolalpha;using std::cout;using std::endl;cout << "std::complex<double>对象测试: " << endl;std::complex<double> c1;std::complex<double> c2(3, -4);std::complex<double> c3(c2);std::complex<double> c4 = c2;const std::complex<double> c5(3.5);cout << "c1 = " << c1 << endl;cout << "c2 = " << c2 << endl;cout << "c3 = " << c3 << endl;cout << "c4 = " << c4 << endl;cout << "c5.real = " << c5.real()<< ", c5.imag = " << c5.imag() << endl<< endl;cout << "复数运算测试: " << endl;cout << "abs(c2) = " << abs(c2) << endl;c1 += c2;cout << "c1 += c2, c1 = " << c1 << endl;cout << boolalpha;cout << "c1 == c2 : " << (c1 == c2) << endl;cout << "c1 != c2 : " << (c1 != c2) << endl;c4 = c2 + c3;cout << "c4 = c2 + c3, c4 = " << c4 << endl;
}

运行测试截图

屏幕截图 2025-10-22 105320

  • 问题1:比较自定义类Complex和标准库模板类complex的用法,在使用形式上,哪一种更简洁?函数和运算内在有关联吗?

  • 回答:后者更简洁,函数和运算有内在关联,二者都是对功能进行封装,后者只是将我们常用的运算封装为运算符,可以认为后者蕴含于前者。

  • 问题2-1:自定义Complex中,output/abs/add/等均设为友元,它们真的需要访问私有数据吗?(回答“是/否”并给出理由)

  • 回答:否,显然output/abs/add/可以使用接口get_real\get_imag,不需要访问私有数据。

  • 问题2-2:标准库std::complex是否把abs设为友元?(查阅cppreference后回答)

  • 回答:否。

  • 问题2-3:什么时候才考虑使用friend?总结你的思考。

  • 回答:函数需要调用类的私有方法或者修改类的私有成员变量时使用friend。

  • 问题3:如果构造对象时禁用=形式,即遇到Complex c4 = c2;编译报错,类Complex的设计应如何调整?

  • 回答:禁用拷贝构造,即Complex(const Complex &other) = default;

实验任务3

源代码PlayControl.h,PlayerControl.cpp,task3.cpp

点击查看代码 PlayerControl.h
#pragma once#include <string>enum class ControlType
{Play,Pause,Next,Prev,Stop,Unknown
};class PlayerControl
{
public:PlayerControl();ControlType parse(const std::string &control_str); // 实现std::string --> ControlType转换void execute(ControlType cmd) const;               // 执行控制操作(以打印输出模拟)static int get_cnt();private:static int total_cnt;
};
点击查看代码 PlayerControl.cpp
#include "PlayerControl.h"
#include <iostream>
#include <algorithm>int PlayerControl::total_cnt = 0;PlayerControl::PlayerControl() {}// 待补足
// 1. 将输入字符串转为小写,实现大小写不敏感
// 2. 匹配"play"/"pause"/"next"/"prev"/"stop"并返回对应枚举
// 3. 未匹配的字符串返回ControlType::Unknown
// 4. 每次成功调用parse时递增total_cnt
ControlType PlayerControl::parse(const std::string &control_str)
{std::string cmd_lower;for (const auto &ch : control_str){cmd_lower += std::tolower(ch);}++total_cnt;if (cmd_lower == "play")return ControlType::Play;else if (cmd_lower == "pause")return ControlType::Pause;else if (cmd_lower == "next")return ControlType::Next;else if (cmd_lower == "prev")return ControlType::Prev;else if (cmd_lower == "stop")return ControlType::Stop;elsereturn ControlType::Unknown;
}void PlayerControl::execute(ControlType cmd) const
{switch (cmd){case ControlType::Play:std::cout << "[play] Playing music...\n";break;case ControlType::Pause:std::cout << "[Pause] Music paused\n";break;case ControlType::Next:std::cout << "[Next] Skipping to next track\n";break;case ControlType::Prev:std::cout << "[Prev] Back to previous track\n";break;case ControlType::Stop:std::cout << "[Stop] Music stopped\n";break;default:std::cout << "[Error] unknown control\n";break;}
}int PlayerControl::get_cnt()
{return total_cnt;
}
点击查看代码 task3.cpp
#include "PlayerControl.h"
#include <iostream>void test()
{PlayerControl controller;std::string control_str;std::cout << "Enter Control: (play/pause/next/prev/stop/quit):\n";while (std::cin >> control_str){if (control_str == "quit")break;ControlType cmd = controller.parse(control_str);controller.execute(cmd);std::cout << "Current Player control: " << PlayerControl::get_cnt() << "\n\n";}
}int main()
{test();
}

运行测试截图

屏幕截图 2025-10-22 114359

实现emoji输出

修改PlayerControl.cpp,其余同上

点击查看代码 PlayerControl.cpp
#include "PlayerControl.h"
#include <iostream>
#include <algorithm>int PlayerControl::total_cnt = 0;PlayerControl::PlayerControl() {}// 待补足
// 1. 将输入字符串转为小写,实现大小写不敏感
// 2. 匹配"play"/"pause"/"next"/"prev"/"stop"并返回对应枚举
// 3. 未匹配的字符串返回ControlType::Unknown
// 4. 每次成功调用parse时递增total_cnt
ControlType PlayerControl::parse(const std::string &control_str)
{std::string cmd_lower;for (const auto &ch : control_str){cmd_lower += std::tolower(ch);}++total_cnt;if (cmd_lower == "play")return ControlType::Play;else if (cmd_lower == "pause")return ControlType::Pause;else if (cmd_lower == "next")return ControlType::Next;else if (cmd_lower == "prev")return ControlType::Prev;else if (cmd_lower == "stop")return ControlType::Stop;elsereturn ControlType::Unknown;
}void PlayerControl::execute(ControlType cmd) const
{switch (cmd){case ControlType::Play:std::cout << "▶️  Playing music...\n";break;case ControlType::Pause:std::cout << "⏸️  Music paused\n";break;case ControlType::Next:std::cout << "⏭️  Skipping to next track\n";break;case ControlType::Prev:std::cout << "⏮️  Back to previous track\n";break;case ControlType::Stop:std::cout << "⏹️  Music stopped\n";break;default:std::cout << "❓  unknown control\n";break;}
}int PlayerControl::get_cnt()
{return total_cnt;
}
运行测试截图

屏幕截图 2025-10-22 114916

实验任务4

源代码Fraction.h,Fraction.cpp,task4.cpp

点击查看代码 Fraction.h
#pragma once#include <string>class Fraction
{
public:Fraction(int _up, int _down = 1);Fraction(const Fraction &f);int get_up() const;int get_down() const;Fraction negative() const;friend void output(const Fraction &f);friend Fraction add(const Fraction &f1, const Fraction &f2);friend Fraction sub(const Fraction &f1, const Fraction &f2);friend Fraction mul(const Fraction &f1, const Fraction &f2);friend Fraction div(const Fraction &f1, const Fraction &f2);private:int up, down;public:static const std::string doc;
};void output(const Fraction &f);
Fraction add(const Fraction &f1, const Fraction &f2);
Fraction sub(const Fraction &f1, const Fraction &f2);
Fraction mul(const Fraction &f1, const Fraction &f2);
Fraction div(const Fraction &f1, const Fraction &f2);
点击查看代码 Fraction.cpp
#include <iostream>
#include <algorithm>
#include "Fraction.h"const std::string Fraction::doc ="Fraction类 v 0.01版.\n""目前仅支持分数对象的构造、输出、加/减/乘/除运算.";Fraction::Fraction(int _up, int _down): up(_up), down(_down)
{int num_gcd = std::__gcd(abs(up), abs(down));up /= num_gcd;down /= num_gcd;
}Fraction::Fraction(const Fraction &f): up(f.up), down(f.down)
{int num_gcd = std::__gcd(abs(up), abs(down));up /= num_gcd;down /= num_gcd;
}int Fraction::get_up() const
{return up;
}int Fraction::get_down() const
{return down;
}Fraction Fraction::negative() const
{return Fraction(-up, down);
}void output(const Fraction &f)
{bool is_negative = (f.up > 0) ^ (f.down > 0);int num_gcd = std::__gcd(abs(f.up), abs(f.down));int new_up = abs(f.up) / num_gcd;int new_down = abs(f.down) / num_gcd;if (new_down == 0){std::cout << "分母不能为0";return;}if (new_up == 0){std::cout << "0";return;}if (is_negative)std::cout << "-";if (new_down == 1){std::cout << new_up;return;}std::cout << new_up << "/" << new_down;
}Fraction add(const Fraction &f1, const Fraction &f2)
{int new_up = f1.up * f2.down + f2.up * f1.down;int new_down = f1.down * f2.down;int num_gcd = std::__gcd(abs(new_up), abs(new_down));return Fraction(new_up / num_gcd, new_down / num_gcd);
}Fraction sub(const Fraction &f1, const Fraction &f2)
{int new_up = f1.up * f2.down - f2.up * f1.down;int new_down = f1.down * f2.down;int num_gcd = std::__gcd(abs(new_up), abs(new_down));return Fraction(new_up / num_gcd, new_down / num_gcd);
}Fraction mul(const Fraction &f1, const Fraction &f2)
{int new_up = f1.up * f2.up;int new_down = f1.down * f2.down;int num_gcd = std::__gcd(abs(new_up), abs(new_down));return Fraction(new_up / num_gcd, new_down / num_gcd);
}Fraction div(const Fraction &f1, const Fraction &f2)
{int new_up = f1.up * f2.down;int new_down = f1.down * f2.up;int num_gcd = std::__gcd(abs(new_up), abs(new_down));return Fraction(new_up / num_gcd, new_down / num_gcd);
}
点击查看代码 task4.cpp
#include "Fraction.h"
#include <iostream>void test1();
void test2();int main()
{std::cout << "测试1: Fraction类基础功能测试\n";test1();std::cout << "\n测试2: 分母为0测试: \n";test2();
}void test1()
{using std::cout;using std::endl;cout << "Fraction类测试: " << endl;cout << Fraction::doc << endl<< endl;Fraction f1(5);Fraction f2(3, -4), f3(-18, 12);Fraction f4(f3);cout << "f1 = ";output(f1);cout << endl;cout << "f2 = ";output(f2);cout << endl;cout << "f3 = ";output(f3);cout << endl;cout << "f4 = ";output(f4);cout << endl;const Fraction f5(f4.negative());cout << "f5 = ";output(f5);cout << endl;cout << "f5.get_up() = " << f5.get_up()<< ", f5.get_down() = " << f5.get_down() << endl;cout << "f1 + f2 = ";output(add(f1, f2));cout << endl;cout << "f1 - f2 = ";output(sub(f1, f2));cout << endl;cout << "f1 * f2 = ";output(mul(f1, f2));cout << endl;cout << "f1 / f2 = ";output(div(f1, f2));cout << endl;cout << "f4 + f5 = ";output(add(f4, f5));cout << endl;
}void test2()
{using std::cout;using std::endl;Fraction f6(42, 55), f7(0, 3);cout << "f6 = ";output(f6);cout << endl;cout << "f7 = ";output(f7);cout << endl;cout << "f6 / f7 = ";output(div(f6, f7));cout << endl;
}

运行测试截图

屏幕截图 2025-10-22 120509

  • 问题:分数的输出和计算,output/add/sub/mul/div,你选择的是哪一种设计方案?(友元/自由函数/命名空间+自由函数/类+static)你的决策理由?如友元方案的优缺点、静态成员函数方案的适用场景、命名空间方案的考虑因素等。

  • 回答:友元函数。考虑到分数在计算后可能出现需要化简的情况,且最简分数的存储优于非最简分数(数值可能越界),因此使用友元函数以实现计算后对分数进行简化;由于友元函数可以访问私有成员,故类的安全性有所降低,存在数据被非法修改的风险。综合以上两点,采用友元函数的方案或许更优。

实验总结

  • task1中删去友元函数func的普通声明,则编译报错;但是在如task2中删去友元函数output的普通声明,则编译不会报错。结合以上两种情况,发现后者编译不会报错的原因是output有类Complex的参数,使得编译器可以找到其声明。

  • 对于task4,在构造或计算完成后,都应判断分母是否为非0,且进行化简。

  • 诸如output/add/sub/mul/div,可以通过重载运算符实现。

http://www.hskmm.com/?act=detail&tid=36537

相关文章:

  • JS学习记录
  • 2025年10月小红书代理商推荐榜:官方授权与实战案例对比评测
  • 小波函数多尺度变换的 Curvelet 变换
  • 2025年10月西安种植牙医院推荐榜:五强对比评测
  • 2025年10月短视频营销公司对比评测榜:孙圈圈领衔增长型IP服务排行
  • 2025年10月抖音代理商榜单:本地推与千川服务能力对比
  • 三、阅读笔记三:提升开发效率的利器
  • 20232302 2025-2026-1《网络与系统攻防技术》实验二实验报告
  • 2025年10月医用面膜产品推荐:权威对比评测榜揭晓前五强
  • P11024 做题报告
  • 多模态数据湖技术深化,Data Agent新能力发布!“认知”将决定企业上限
  • 2025年10月投资纠纷律师推荐:五强榜单对比评测与选择指南
  • Web刷题篇-1 [BJDCTF2020]Easy MD5
  • 云斗 YDR Special# 004 S 模拟赛
  • Berry.Live:开箱即用的.NET直播流媒体服务器
  • 2025年10月上海ICL医生推荐榜:王晓瑛领衔五强对比
  • doris集成vertica 数据源catalog
  • JUnit 6.0.0发布:Java 17基线、取消API与Kotlin协程支持
  • 详细介绍:老题新解|合法C标识符
  • 2025年10月消泡剂厂家推荐:权威榜单一网打尽
  • 国产化Excel开发组件Spire.XLS教程:使用Python将TXT文件转换为CSV
  • VMware Holodeck 9.0.1.0 发布 - 自动化部署 VCF 实验环境
  • [题解]meal
  • CADSoftTools发布两款重要更新:CAD VCL Multiplatform 16.2 与 CAD .NET 16全新发布
  • linux常用命令 - 实践
  • 2025年10月河道防撞护栏厂家全景解析报告,基于专业测评的技术、性能及市场优势深度分析
  • 在 Linux 系统上安装 Miniconda、安装 Xinference,并设置 Xinference 开机自启动
  • 作业三(结对编程)-小学四则运算题目生成与判卷(Python + 可视化)
  • 无穷小比较、等价无穷小替换
  • 【项目复现上新】Karpathy大神开源GitHub高分项目NanoChat!仅用100美元+8000行代码手搓ChatGPT