OOP-实验2
实验任务1
源代码task1
1 // 类T的声明、友元函数声明 2 3 #pragma once 4 5 #include <string> 6 7 // 类T: 声明 8 class T 9 { 10 // 对象属性、方法 11 public: 12 T(int x = 0, int y = 0); // 普通构造函数 13 T(const T &t); // 复制构造函数 14 T(T &&t); // 移动构造函数 15 ~T(); // 析构函数 16 17 void adjust(int ratio); // 按系数成倍调整数据 18 void display() const; // 以(m1, m2)形式显示T类对象信息 19 20 private: 21 int m1, m2; 22 23 // 类属性、方法 24 public: 25 static int get_cnt(); // 显示当前T类对象总数 26 27 static const std::string doc; // 类T的描述信息 28 static const int max_cnt; // 类T对象上限 29 30 private: 31 static int cnt; // 当前类T对象数目 32 33 friend void func(); // 类T友元函数声明 34 }; 35 36 // 普通函数声明 37 void func(); 38 39 const std::string T::doc{"a simple class sample"}; 40 const int T::max_cnt = 999; 41 int T::cnt = 0;
1 // 类T的实现、友元函数实现 2 3 #include "T.h" 4 #include <iostream> 5 #include <string> 6 7 // 类T实现 8 9 // static成员数据类外初始化 10 11 12 // 类方法 13 int T::get_cnt() 14 { 15 return cnt; 16 } 17 18 // 对象方法 19 T::T(int x, int y) : m1{x}, m2{y} 20 { 21 cnt++; 22 std::cout << "T constructor called." << std::endl; 23 } 24 25 T::T(const T &t) : m1{t.m1}, m2{t.m2} 26 { 27 cnt++; 28 std::cout << "T copy constructor called." << std::endl; 29 } 30 31 T::T(T &&t) : m1{t.m1}, m2{t.m2} 32 { 33 cnt++; 34 std::cout << "T move constructor called." << std::endl; 35 } 36 37 T::~T() 38 { 39 cnt--; 40 std::cout << "T destructor called." << std::endl; 41 } 42 43 void T::adjust(int ratio) 44 { 45 m1 *= ratio; 46 m2 *= ratio; 47 } 48 49 void T::display() const 50 { 51 std::cout << "(" << m1 << ", " << m2 << ")"; 52 } 53 54 // 普通函数实现 55 void func() 56 { 57 T t5(42); 58 t5.m2 = 2049; 59 std::cout << "t5 = "; 60 t5.display(); 61 std::cout << std::endl; 62 std::cout << "func: T objects' current count: " << T::get_cnt() << std::endl; 63 }
1 // 简单类T的定义和测试 2 // 测试模块、main函数 3 4 #include "T.h" 5 #include <iostream> 6 #include <string> 7 8 // 测试函数声明 9 void test_T(); 10 11 int main() 12 { 13 std::cout << "test Class T: " << std::endl; 14 test_T(); 15 16 std::cout << std::endl; 17 std::cout << "test friend func: " << std::endl; 18 func(); 19 } 20 21 void test_T() 22 { 23 using namespace std; 24 25 cout << "T info: " << T::doc << endl; 26 cout << "T objects'max count: " << T::max_cnt << endl; 27 cout << "T objects'current count: " << T::get_cnt() << endl; 28 cout << endl; 29 30 T t1; 31 cout << "t1 = "; 32 t1.display(); 33 cout << endl; 34 35 T t2(3, 4); 36 cout << "t2 = "; 37 t2.display(); 38 cout << endl; 39 40 T t3(t2); 41 t3.adjust(2); 42 cout << "t3 = "; 43 t3.display(); 44 cout << endl; 45 46 T t4(std::move(t2)); 47 cout << "t4 = "; 48 t4.display(); 49 cout << endl; 50 51 cout << "test: T objects'current count: " << T::get_cnt() << endl; 52 }
运行测试截图

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

不能;
原因:此时友元函数func只在类内进行了声明,而在类外是不可见的;因此如果想要在类外使用它就必须另外在类外声明。
问题2:T.h中,line9-12给出了各种构造函数、析构函数。总结它们各自的功能、调用时机。
默认构造函数:(1)功能:调用时无须提供参数;如果类中没有写构造函数,编译器会自动生成一个隐含的默认构造函数,该构造函数的参数列表和函数体皆为空,无法赋初值;如果类中已声明了任意的构造函数(无论是否有参数),编译器都不会再为之生成隐含的构造函数。(2)调用时机:当以不带任何参数的形式创建对象时
普通构造函数:(1)功能:用已给定的参数来初始化对象。(2)调用时机:当以带参数的形式创建对象时
复制构造函数:(1)功能:用同一类的另一个对象来初始化本对象。(2)调用时机:当用同类的一个对象初始化另一个新对象时
移动构造函数:(1)功能:右值引用同类的另一个对象来初始化新对象。(2)调用时机:当使用右值引用来初始化新对象时
析构函数:(1)功能:释放对象所占的内存及完成相关的“清理”工作。(2)调用时机:在对象生存期结束时由编译器自动调用
问题3:T.cpp中,line13-15,剪切到T.h的末尾,重新编译,程序能否正确编译。如不能,以截图形式给出报错信息,分析原因。

不能;
原因:将对 static 成员数据的初始化定义在 T.h 中,而 T.h 又被 T.cpp、task1.cpp 包含,因此相当于静态成员数据被多次定义,多重定义导致程序无法正确编译。
实验任务2
源代码task2
1 #include <string> 2 3 class Complex 4 { 5 // 类属性、方法 6 public: 7 static const std::string doc; 8 9 // 对象属性、方法 10 public: 11 Complex(double real = 0.0, double imag = 0.0); 12 Complex(const Complex &c); 13 14 double get_real() const; 15 double get_imag() const; 16 void add(const Complex &c); 17 18 private: 19 double real, imag; 20 21 // 友元函数 22 friend void output(const Complex &c); 23 friend double abs(const Complex &c); 24 friend Complex add(const Complex &c1, const Complex &c2); 25 friend bool is_equal(const Complex &c1, const Complex &c2); 26 friend bool is_not_equal(const Complex &c1, const Complex &c2); 27 }; 28 29 // 友元函数声明 30 void output(const Complex &c); 31 double abs(const Complex &c); 32 Complex add(const Complex &c1, const Complex &c2); 33 bool is_equal(const Complex &c1, const Complex &c2); 34 bool is_not_equal(const Complex &c1, const Complex &c2);
1 #include "Complex.h" 2 #include <iostream> 3 #include <cmath> 4 5 // 类属性、方法实现 6 const std::string Complex::doc{"a simplified complex class"}; 7 8 // 对象方法实现 9 Complex::Complex(double real, double imag) : real{real}, imag{imag} {} 10 11 Complex::Complex(const Complex &c) : real{c.real}, imag{c.imag} {} 12 13 double Complex::get_real() const 14 { 15 return real; 16 } 17 18 double Complex::get_imag() const 19 { 20 return imag; 21 } 22 23 void Complex::add(const Complex &c) 24 { 25 real += c.real; 26 imag += c.imag; 27 } 28 29 // 友元函数实现 30 void output(const Complex &c) 31 { 32 if (c.imag >= 0) 33 std::cout << c.get_real() << " + " << c.get_imag() << "i"; 34 else 35 std::cout << c.get_real() << " - " << c.get_imag() * (-1.0) << "i"; 36 } 37 38 double abs(const Complex &c) 39 { 40 return std::sqrt(c.get_real() * c.get_real() + c.get_imag() * c.get_imag()); 41 } 42 43 Complex add(const Complex &c1, const Complex &c2) 44 { 45 Complex c; 46 c.real = c1.real + c2.real; 47 c.imag = c1.imag + c2.imag; 48 49 return c; 50 } 51 52 bool is_equal(const Complex &c1, const Complex &c2) 53 { 54 if (c1.real == c2.real && c1.imag == c2.imag) 55 return true; 56 return false; 57 } 58 59 bool is_not_equal(const Complex &c1, const Complex &c2) 60 { 61 if (is_equal(c1, c2)) 62 return false; 63 return true; 64 }
1 // 不适用C++标准库提供的复数模板类,设计并实现一个简化版复数类Complex 2 3 #ifdef _WIN32 4 #include <windows.h> 5 #endif 6 7 #include "Complex.h" 8 #include <iostream> 9 #include <iomanip> 10 #include <complex> 11 12 void test_Complex(); 13 void test_std_complex(); 14 void setConsoleUTF8() 15 { 16 #ifdef _WIN32 17 SetConsoleOutputCP(65001); 18 SetConsoleCP(65001); 19 #endif 20 } 21 22 int main() 23 { 24 setConsoleUTF8(); 25 26 std::cout << "*******测试1: 自定义类Complex*******" << std::endl; 27 test_Complex(); 28 29 std::cout << std::endl; 30 std::cout << "*******测试2: 标准库模板类complex*******" << std::endl; 31 test_std_complex(); 32 } 33 34 void test_Complex() 35 { 36 using namespace std; 37 38 cout << "类成员测试: " << endl; 39 cout << Complex::doc << endl 40 << endl; 41 42 cout << "Complex对象测试: " << endl; 43 Complex c1; 44 Complex c2(3, -4); 45 Complex c3(c2); 46 Complex c4 = c2; 47 const Complex c5(3.5); 48 49 cout << "c1 = "; 50 output(c1); 51 cout << endl; 52 53 cout << "c2 = "; 54 output(c2); 55 cout << endl; 56 57 cout << "c3 = "; 58 output(c3); 59 cout << endl; 60 61 cout << "c4 = "; 62 output(c4); 63 cout << endl; 64 65 cout << "c5.real = " << c5.get_real() 66 << ", c5.imag = " << c5.get_imag() << endl 67 << endl; 68 69 cout << "复数运算测试: " << endl; 70 71 cout << "abs(c2) = " << abs(c2) << endl; 72 73 c1.add(c2); 74 cout << "c1 += c2, c1 = "; 75 output(c1); 76 cout << endl; 77 78 cout << boolalpha; 79 cout << "c1 == c2 : " << is_equal(c1, c2) << endl; 80 cout << "c1 != c2 : " << is_not_equal(c1, c2) << endl; 81 82 c4 = add(c2, c3); 83 cout << "c4 = c2 + c3, c4 = "; 84 output(c4); 85 cout << endl; 86 } 87 88 void test_std_complex() 89 { 90 using namespace std; 91 92 cout << "std::complex<double>对象测试: " << endl; 93 std::complex<double> c1; 94 std::complex<double> c2(3, -4); 95 std::complex<double> c3(c2); 96 std::complex<double> c4 = c2; 97 const std::complex<double> c5(3.5); 98 99 cout << "c1 = " << c1 << endl; 100 cout << "c2 = " << c2 << endl; 101 cout << "c3 = " << c3 << endl; 102 cout << "c4 = " << c4 << endl; 103 104 cout << "c5.real = " << c5.real() 105 << ", c5.imag = " << c5.imag() << endl 106 << endl; 107 108 cout << "复数运算测试: " << endl; 109 cout << "abs(c2) = " << abs(c2) << endl; 110 c1 += c2; 111 cout << "c1 += c2, c1 = " << c1 << endl; 112 cout << boolalpha; 113 cout << "c1 == c2 : " << (c1 == c2) << endl; 114 cout << "c1 != c2 : " << (c1 != c2) << endl; 115 c4 = c2 + c3; 116 cout << "c4 = c2 + c3, c4 = " << c4 << endl; 117 }
运行测试截图

回答问题
问题1:比较自定义类 Complex 和标准库模板类 complex 的用法,在使用形式上,哪一种更简洁?函数和运算内在有关联吗?
标准库模板类 complex 更简洁;
函数和运算内在是等价的
问题2-1:自定义 Complex 中,output/abs/add/ 等均设为友元,它们真的需要访问私有数据吗?(回答"是/否"并给出理由)
不需要;
理由:output/abs/add 函数的参数都是一个 Complex 类的对象,当我们需要使用该对象的私有数据时,我们可以通过它的成员函数 get_real() 和 get_imag() 获取,没有必要直接访问私有数据,这样还破坏了类的封装性和隐藏性
问题2-2:标准库 std::complex 是否把 abs 设为友元?
否
问题2-3:什么时候才考虑使用 friend ? 总结你的思考。
使用公有接口会导致较大的性能开销时;当运算符需要在左右操作数上有对称的访问权限时
问题3:如果构造对象时禁用 = 形式,即遇到 Complex c4 = c2; 编译报错,类 Complex 的设计应如何调整?
此时禁用了复制构造函数的隐式拷贝,可以选择复制构造函数的显式拷贝,如 Complex c4(c2);
实验任务3
源代码task3
1 // 播放控制类PlayerControl声明 2 3 #pragma once 4 5 #include <string> 6 7 enum class ControlType 8 { 9 Play, 10 Pause, 11 Next, 12 Prev, 13 Stop, 14 Unknown 15 }; 16 17 class PlayerControl 18 { 19 public: 20 PlayerControl(); 21 22 // 实现std::string --> ControlType转换 23 ControlType parse(const std::string &control_str); 24 void execute(ControlType cmd) const; // 执行控制操作(以打印输出模拟) 25 26 static int get_cnt(); 27 28 private: 29 static int total_cnt; 30 };
1 // 播放控制类PlayerControl实现 2 3 #include "PlayerControl.h" 4 #include <iostream> 5 #include <algorithm> 6 #include <cctype> 7 8 int PlayerControl::total_cnt = 0; 9 10 PlayerControl::PlayerControl() {} 11 12 ControlType PlayerControl::parse(const std::string &control_str) 13 { 14 using namespace std; 15 16 string control_str_trans; 17 18 // 将control_str全部转为小写 19 for (auto &c : control_str) 20 { 21 char t = tolower(c); 22 control_str_trans += t; 23 } 24 25 if (control_str_trans == "play") 26 { 27 total_cnt++; 28 return ControlType::Play; 29 } 30 31 else if (control_str_trans == "pause") 32 { 33 total_cnt++; 34 return ControlType::Pause; 35 } 36 37 else if (control_str_trans == "next") 38 { 39 total_cnt++; 40 return ControlType::Next; 41 } 42 43 else if (control_str_trans == "prev") 44 { 45 total_cnt++; 46 return ControlType::Prev; 47 } 48 49 else if (control_str_trans == "stop") 50 { 51 total_cnt++; 52 return ControlType::Stop; 53 } 54 55 else 56 return ControlType::Unknown; 57 } 58 59 void PlayerControl::execute(ControlType cmd) const 60 { 61 switch (cmd) 62 { 63 case ControlType::Play: 64 std::cout << "🎵 Playing music..." << std::endl; 65 break; 66 67 case ControlType::Pause: 68 std::cout << "[Pause] Music paused." << std::endl; 69 break; 70 71 case ControlType::Next: 72 std::cout << "[Next] Skipping to next track." << std::endl; 73 break; 74 75 case ControlType::Prev: 76 std::cout << "[Prev] Back to previous track." << std::endl; 77 78 case ControlType::Stop: 79 std::cout << "[Stop] Music stopped." << std::endl; 80 break; 81 82 default: 83 std::cout << "[Error] unknown control." << std::endl; 84 break; 85 } 86 } 87 88 int PlayerControl::get_cnt() 89 { 90 return total_cnt; 91 }
1 // 测试模块 + main 2 3 #include "PlayerControl.h" 4 #include <iostream> 5 6 void test() 7 { 8 PlayerControl controller; 9 std::string control_str; 10 std::cout << "Enter Control: (play/pause/next/prev/stop/quit): " << std::endl; 11 12 while (std::cin >> control_str) 13 { 14 if (control_str == "quit") 15 break; 16 17 ControlType cmd = controller.parse(control_str); 18 controller.execute(cmd); 19 std::cout << "Current Player control: " << PlayerControl::get_cnt() << std::endl 20 << std::endl; 21 } 22 } 23 24 int main() 25 { 26 test(); 27 return 0; 28 }
控制台输出 emoji :添加 UTF8 编码代码
1 // 播放控制类PlayerControl声明 2 3 #pragma once 4 5 #include <string> 6 7 enum class ControlType 8 { 9 Play, 10 Pause, 11 Next, 12 Prev, 13 Stop, 14 Unknown 15 }; 16 17 class PlayerControl 18 { 19 public: 20 PlayerControl(); 21 22 // 实现std::string --> ControlType转换 23 ControlType parse(const std::string &control_str); 24 void execute(ControlType cmd) const; // 执行控制操作(以打印输出模拟) 25 26 static int get_cnt(); 27 28 private: 29 static int total_cnt; 30 };
1 // 播放控制类PlayerControl实现 2 3 #include "PlayerControl.h" 4 #include <iostream> 5 #include <algorithm> 6 #include <cctype> 7 8 int PlayerControl::total_cnt = 0; 9 10 PlayerControl::PlayerControl() {} 11 12 ControlType PlayerControl::parse(const std::string &control_str) 13 { 14 using namespace std; 15 16 string control_str_trans; 17 18 // 将control_str全部转为小写 19 for (auto &c : control_str) 20 { 21 char t = tolower(c); 22 control_str_trans += t; 23 } 24 25 if (control_str_trans == "play") 26 { 27 total_cnt++; 28 return ControlType::Play; 29 } 30 31 else if (control_str_trans == "pause") 32 { 33 total_cnt++; 34 return ControlType::Pause; 35 } 36 37 else if (control_str_trans == "next") 38 { 39 total_cnt++; 40 return ControlType::Next; 41 } 42 43 else if (control_str_trans == "prev") 44 { 45 total_cnt++; 46 return ControlType::Prev; 47 } 48 49 else if (control_str_trans == "stop") 50 { 51 total_cnt++; 52 return ControlType::Stop; 53 } 54 55 else 56 return ControlType::Unknown; 57 } 58 59 void PlayerControl::execute(ControlType cmd) const 60 { 61 switch (cmd) 62 { 63 case ControlType::Play: 64 std::cout << "🎵 Playing music..." << std::endl; 65 break; 66 67 case ControlType::Pause: 68 std::cout << "⏸ Music paused." << std::endl; 69 break; 70 71 case ControlType::Next: 72 std::cout << "⏭ Skipping to next track." << std::endl; 73 break; 74 75 case ControlType::Prev: 76 std::cout << "⏮ Back to previous track." << std::endl; 77 break; 78 79 case ControlType::Stop: 80 std::cout << "⏹ Music stopped." << std::endl; 81 break; 82 83 default: 84 std::cout << "❌ unknown control." << std::endl; 85 break; 86 } 87 } 88 89 int PlayerControl::get_cnt() 90 { 91 return total_cnt; 92 }
1 // 测试模块 + main 2 3 #ifdef _WIN32 4 #include <windows.h> 5 #endif 6 7 #include "PlayerControl.h" 8 #include <iostream> 9 10 void setConsoleUTF8() 11 { 12 #ifdef _WIN32 13 SetConsoleOutputCP(65001); 14 SetConsoleCP(65001); 15 #endif 16 } 17 18 void test() 19 { 20 PlayerControl controller; 21 std::string control_str; 22 std::cout << "Enter Control: (play/pause/next/prev/stop/quit): " << std::endl; 23 24 while (std::cin >> control_str) 25 { 26 if (control_str == "quit") 27 break; 28 29 ControlType cmd = controller.parse(control_str); 30 controller.execute(cmd); 31 std::cout << "Current Player control: " << PlayerControl::get_cnt() << std::endl 32 << std::endl; 33 } 34 } 35 36 int main() 37 { 38 setConsoleUTF8(); 39 test(); 40 return 0; 41 }
运行测试截图

实验任务4
源代码task4
1 #pragma once 2 3 #include <string> 4 5 class Fraction 6 { 7 // 类属性 8 public: 9 static const std::string doc; 10 11 // 对象属性、方法 12 public: 13 Fraction(int up, int down = 1); 14 Fraction(const Fraction &f); 15 16 int get_up() const; 17 int get_down() const; 18 Fraction negative(); 19 20 private: 21 int up, down; 22 }; 23 24 // 工具函数 25 namespace Utils 26 { 27 void output(const Fraction &f); 28 Fraction add(const Fraction &f1, const Fraction &f2); 29 Fraction sub(const Fraction &f1, const Fraction &f2); 30 Fraction mul(const Fraction &f1, const Fraction &f2); 31 Fraction div(const Fraction &f1, const Fraction &f2); 32 Fraction trans(Fraction &f); 33 }
1 #include "Fraction.h" 2 #include <iostream> 3 #include <string> 4 #include <cmath> 5 6 const std::string Fraction::doc{ 7 "Fraction类 v 0.01版.\n" 8 "目前仅支持分数对象的构造、输出、加/减/乘/除运算."}; // 字符串中间不能直接换行 9 10 Fraction::Fraction(int up, int down) : up{up}, down{down} {} 11 12 Fraction::Fraction(const Fraction &f) : up{f.up}, down{f.down} {} 13 14 int Fraction::get_up() const 15 { 16 return up; 17 } 18 19 int Fraction::get_down() const 20 { 21 return down; 22 } 23 24 Fraction Fraction::negative() 25 { 26 return Fraction(-up, down); 27 } 28 29 void Utils::output(const Fraction &f) 30 { 31 int up = f.get_up(); 32 int down = f.get_down(); 33 34 if (down == 0) 35 { 36 std::cout << "分母不能为0" << std::endl; 37 return; 38 } 39 40 if (down == 1) 41 { 42 std::cout << up; 43 return; 44 } 45 46 if (up == 0) 47 { 48 std::cout << up; 49 return; 50 } 51 52 int a = abs(f.get_up()); 53 int b = abs(f.get_down()); 54 // 求最大公约数 55 while (b != 0) 56 { 57 int t = b; 58 b = a % b; 59 a = t; 60 } 61 up /= a; 62 down /= a; 63 64 if (down < 0) 65 { 66 up = -up; 67 down = -down; 68 } 69 70 std::cout << up << "/" << down; 71 } 72 73 Fraction Utils::add(const Fraction &f1, const Fraction &f2) 74 { 75 int down = f1.get_down() * f2.get_down(); 76 int up = f1.get_up() * f2.get_down() + f2.get_up() * f1.get_down(); 77 78 return Fraction(up, down); 79 } 80 81 Fraction Utils::sub(const Fraction &f1, const Fraction &f2) 82 { 83 int down = f1.get_down() * f2.get_down(); 84 int up = f1.get_up() * f2.get_down() - f2.get_up() * f1.get_down(); 85 86 return Fraction(up, down); 87 } 88 89 Fraction Utils::mul(const Fraction &f1, const Fraction &f2) 90 { 91 int up = f1.get_up() * f2.get_up(); 92 int down = f1.get_down() * f2.get_down(); 93 94 return Fraction(up, down); 95 } 96 97 Fraction Utils::div(const Fraction &f1, const Fraction &f2) 98 { 99 int up = f1.get_up() * f2.get_down(); 100 int down = f1.get_down() * f2.get_up(); 101 102 return Fraction(up, down); 103 } 104 105 Fraction Utils::trans(Fraction &f) 106 { 107 int up = f.get_up(); 108 int down = f.get_down(); 109 int a = abs(f.get_up()); 110 int b = abs(f.get_down()); 111 // 求最大公约数 112 while (b != 0) 113 { 114 int t = b; 115 b = a % b; 116 a = t; 117 } 118 up /= a; 119 down /= a; 120 121 return Fraction(up, down); 122 }
1 #include "Fraction.h" 2 #include <iostream> 3 4 #ifdef _WIN32 5 #include <windows.h> 6 #endif 7 8 void test1(); 9 void test2(); 10 void setConsoleUTF8() 11 { 12 #ifdef _WIN32 13 SetConsoleOutputCP(65001); 14 SetConsoleCP(65001); 15 #endif 16 } 17 18 int main() 19 { 20 setConsoleUTF8(); 21 22 std::cout << "测试1: Fraction类基础功能测试" << std::endl; 23 test1(); 24 25 std::cout << std::endl; 26 std::cout << "测试2: 分母为0测试: " << std::endl; 27 test2(); 28 } 29 30 void test1() 31 { 32 using namespace std; 33 34 cout << "Fraction类测试: " << endl; 35 cout << Fraction::doc << endl 36 << endl; 37 38 Fraction f1(5); 39 Fraction f2(3, -4), f3(-18, 12); 40 Fraction f4(f3); 41 42 cout << "f1 = "; 43 Utils::output(f1); 44 cout << endl; 45 46 cout << "f2 = "; 47 Utils::output(f2); 48 cout << endl; 49 50 cout << "f3 = "; 51 Utils::output(f3); 52 cout << endl; 53 54 cout << "f4 = "; 55 Utils::output(f4); 56 cout << endl; 57 58 Fraction f5(f4.negative()); 59 cout << "f5 = "; 60 Utils::output(f5); 61 cout << endl; 62 63 f5 = Utils::trans(f5); 64 cout << "f5.get_up() = " << f5.get_up() 65 << ", f5.get_down() = " << f5.get_down() << endl; 66 67 cout << "f1 + f2 = "; 68 Utils::output(Utils::add(f1, f2)); 69 cout << endl; 70 71 cout << "f1 - f2 = "; 72 Utils::output(Utils::sub(f1, f2)); 73 cout << endl; 74 75 cout << "f1 * f2 = "; 76 Utils::output(Utils::mul(f1, f2)); 77 cout << endl; 78 79 cout << "f1 / f2 = "; 80 Utils::output(Utils::div(f1, f2)); 81 cout << endl; 82 83 cout << "f4 + f5 = "; 84 Utils::output(Utils::add(f4, f5)); 85 cout << endl; 86 } 87 88 void test2() 89 { 90 using namespace std; 91 92 Fraction f6(42, 55), f7(0, 3); 93 94 cout << "f6 = "; 95 Utils::output(f6); 96 cout << endl; 97 98 cout << "f7 = "; 99 Utils::output(f7); 100 cout << endl; 101 102 cout << "f6 / f7 = "; 103 Utils::output(Utils::div(f6, f7)); 104 cout << endl; 105 }
运行测试截图

回答问题
问题:分数的输出和计算,output/add/sub/mul/div,你选择的是哪一种设计方案?(友元/自由函数/命名空间+自由函数/类+static)你的决策理由?如友元方案的优缺点、静态成员函数方案的适用场景、命名空间方案的考虑因素等。
选择的是 命名空间 + 自由函数
理由:友元方案会破坏类的封装性和隐藏性;静态成员函数方案会使类内有过多的函数方法,且不利于后续的扩展和维护;命名空间方案扩展性和维护性好,且更有利于团队协作
实验总结
1. 即使在类内声明了友元函数,在类外还要再声明一次,写在 X.h 文件里即可
2. 采用多文件结构时,静态数据成员的类外初始化不能写在 X.h 里,会导致多重定义,应该写在 X.cpp 里
3. 当控制台输出中文时显示乱码时,解决方法:
1 // task.cpp 2 3 #ifdef _WIN32 4 #include <windows.h> 5 #endif 6 7 void setConsoleUTF8() 8 { 9 #ifdef _WIN32 10 SetConsoleOutputCP(65001); 11 SetConsoleCP(65001); 12 #endif 13 } 14 15 int main() 16 { 17 setConsoleUTF8(); 18 }
4. 字符串中间不能直接换行,应该自换行处分开为多个字符串:
const std::string Fraction::doc{ "Fraction类 v 0.01版.\n" "目前仅支持分数对象的构造、输出、加/减/乘/除运算."};


浙公网安备 33010602011771号