实验2
任务1:
T.h
1 #pragma once 2 #include<string> 3 4 //类T声明 5 class T { 6 //对象属性、方法 7 public: 8 T(int x = 0,int y = 0); //普通构造函数 9 T(const T &t); //复制构造函数 10 T(T &&t); //移动构造函数 11 ~T(); //析构函数 12 13 void adjust(int ratio); //按系数成倍调整数据 14 void display() const; 15 16 private: 17 int m1,m2; 18 19 //类属性、方法 20 public: 21 static int get_cnt(); 22 23 public: 24 static const std::string doc; 25 static const int max_cnt; 26 27 private: 28 static int cnt; 29 30 friend void func(); 31 }; 32 33 void func();
T.cpp
1 #include "T.h" 2 #include<iostream> 3 #include<string> 4 5 const std::string T::doc{"a simple class sample"}; 6 const int T::max_cnt = 999; 7 int T::cnt = 0; 8 9 int T::get_cnt() { 10 return cnt; 11 } 12 13 T::T(int x,int y): m1{x},m2{y} { 14 ++cnt; 15 std::cout << "T constructor called.\n"; 16 } 17 18 T::T(const T &t):m1{t.m1},m2{t.m2} { 19 ++cnt; 20 std::cout << "T copy constructor called.\n"; 21 } 22 23 T::T(T &&t):m1{t.m1},m2{t.m2} { 24 ++cnt; 25 std::cout << "T move constructor called.\n"; 26 } 27 28 T::~T() { 29 --cnt; 30 std::cout << "T destructor called.\n"; 31 } 32 33 void T::adjust(int ratio) { 34 m1 *= ratio; 35 m2 *= ratio; 36 } 37 38 void T::display() const { 39 std::cout << "(" << m1 << ", " << m2 << ")" ; 40 } 41 42 void func() { 43 T t5(42); 44 t5.m2 = 2049; 45 std::cout << "t5 = "; t5.display(); std::cout << '\n'; 46 }
task1.cpp
1 #include "T.h" 2 #include <iostream> 3 4 void test_T(); 5 6 int main() { 7 std::cout << "test Class T: \n"; 8 test_T(); 9 10 std::cout << "\ntest friend func: \n"; 11 func(); 12 } 13 14 void test_T() { 15 using std::cout; 16 using std::endl; 17 18 cout << "T info: " << T::doc << endl; 19 cout << "T objects'max count: " << T::max_cnt << endl; 20 cout << "T objects'current count: " << T::get_cnt() << endl << endl; 21 22 T t1; 23 cout << "t1 = "; t1.display(); cout << endl; 24 25 T t2(3, 4); 26 cout << "t2 = "; t2.display(); cout << endl; 27 28 T t3(t2); 29 t3.adjust(2); 30 cout << "t3 = "; t3.display(); cout << endl; 31 32 T t4(std::move(t2)); 33 cout << "t4 = "; t4.display(); cout << endl; 34 35 cout << "test: T objects'current count: " << T::get_cnt() << endl; 36 }

#问题1:
T.h中,在类T内部,已声明 func 是T的友元函数。在类外部,去掉line36,重新编译,程序能否正常运行。 如果能,回答YES;如果不能,以截图形式提供编译报错信息,说明原因.
YES
##问题2:
T.h中,line9-12给出了各种构造函数、析构函数。总结它们各自的功能、调用时机。
(1)普通构造函数(T(int x =0 ,int y = 0)):
功能:创建对象并初始化成员变量。
调用时机:创建新对象时。
(2)复制构造函数(T(const T &t)):
功能:创建新对象,并将已经存在的对象数据复制给新对象。
调用时机:用已有的对象初始化新对象时。
(3)移动构造函数(T(T && t)):
功能:创建新对象,并将原对象的数据转移给新对象,原对象处于有效但未定义状态。
调用时机:将已有的对象转移给新对象时。
(4)析构函数(~T()):
功能:对象销毁时清理资源。
调用时机:函数调用结束时,逆序调用。
###问题3:
T.cpp中,line13-15,剪切到T.h的末尾,重新编译,程序能否正确编译。 如不能,以截图形式给出报错信息,分析原因。
YES
任务2:
Complex.h
1 #pragma once 2 3 #include<string> 4 5 //类Complex的声明 6 class Complex { 7 //对象的属性和方法 8 public: 9 Complex(double x = 0,double y = 0); //普通构造函数 10 Complex(const Complex &c); //复制构造函数 11 12 double get_real() const; 13 double get_imag() const; 14 void add(const Complex &c); 15 16 private: 17 double real,imag; 18 19 friend void output(const Complex &c); 20 friend double abs(const Complex &c); 21 friend Complex add(const Complex &c1,const Complex &c2); 22 friend bool is_equal(const Complex &c1,const Complex &c2); 23 friend bool is_not_equal(const Complex &c1,const Complex &c2); 24 25 //类的属性和方法 26 public: 27 static const std::string doc; //类的说明 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);
Complex.cpp
1 #include "Complex.h" 2 #include<iostream> 3 #include<string> 4 #include<cmath> 5 #include<iomanip> 6 7 //类Complex的实现 8 9 //static 成员类外初始化 10 const std::string Complex::doc{"a simple complex class sample"}; 11 12 //对象方法 13 Complex::Complex(double x,double y):real{x},imag{y} {} 14 Complex::Complex(const Complex &c):real{c.real},imag{c.imag} {} 15 double Complex::get_real() const { return real; } 16 double Complex::get_imag() const { return imag; } 17 void Complex::add(const Complex &c) { 18 real += c.real; 19 imag += c.imag; 20 } 21 22 void output(const Complex &c) { 23 std::cout << c.real << " + " << c.imag << "i" << std::endl; 24 } 25 26 double abs(const Complex &c) { 27 double s = c.real * c.real + c.imag * c.imag; 28 //std::cout << std::fixed << std::setprecision(1); 29 return sqrt(s); 30 } 31 32 Complex add(const Complex &c1,const Complex &c2) { 33 double real = c1.real + c2.real; 34 double imag = c1.imag + c2.imag; 35 return Complex(real,imag); 36 } 37 38 bool is_equal(const Complex &c1,const Complex &c2) { 39 if(c1.real == c2.real && c1.imag == c2.imag) 40 return true; 41 else 42 return false; 43 } 44 45 bool is_not_equal(const Complex &c1,const Complex &c2) { 46 if(c1.real == c2.real && c1.imag == c2.imag) 47 return true; 48 else 49 return false; 50 }
task2.cpp
1 #include"Complex.h"
2 #include<iostream> 3 #include<iomanip> 4 #include<complex> 5 6 void test_complex(); 7 void test_std_complex(); 8 9 int main() { 10 std::cout << "*******测试1: 自定义类Complex*******\n"; 11 test_complex(); 12 std::cout << "\n*******测试2: 标准库模板类complex*******\n"; 13 test_std_complex(); 14 } 15 16 void test_complex() { 17 using std::cout; 18 using std::endl; 19 using std::boolalpha; 20 21 cout << "类成员测试:" << endl; 22 cout << Complex::doc << endl <<endl; 23 24 cout << "Complex对象测试:" << endl; 25 Complex c1; 26 Complex c2(3,-4); 27 Complex c3(c2); 28 Complex c4 = c2; 29 const Complex c5(3.5); 30 31 cout << "c1 = "; output(c1); cout << endl; 32 cout << "c2 = "; output(c2); cout << endl; 33 cout << "c3 = "; output(c3); cout << endl; 34 cout << "c4 = "; output(c4); cout << endl; 35 cout << "c5.real = " << c5.get_real() 36 << ",c5.imag = " << c5.get_imag() << endl << endl; 37 38 cout << "复数运算测试: " << endl; 39 cout << "abs(c2) = " << abs(c2) << endl; 40 c1.add(c2); 41 cout << "c1 += c2, c1 = "; output(c1); cout << endl; 42 cout << boolalpha; 43 cout << "c1 == c2 : " << is_equal(c1, c2) << endl; 44 cout << "c1 != c2 : " << is_not_equal(c1, c2) << endl; 45 c4 = add(c2, c3); 46 cout << "c4 = c2 + c3, c4 = "; output(c4); cout << endl; 47 } 48 49 void test_std_complex() { 50 using std::cout; 51 using std::endl; 52 using std::boolalpha; 53 54 cout << "std::complex<double>对象测试: " << endl; 55 std::complex<double> c1; 56 std::complex<double> c2(3, -4); 57 std::complex<double> c3(c2); 58 std::complex<double> c4 = c2; 59 const std::complex<double> c5(3.5); 60 cout << "c1 = " << c1 << endl; 61 cout << "c2 = " << c2 << endl; 62 cout << "c3 = " << c3 << endl; 63 cout << "c4 = " << c4 << endl; 64 cout << "c5.real = " << c5.real() 65 << ", c5.imag = " << c5.imag() << endl << endl; 66 67 cout << "复数运算测试: " << endl; 68 cout << "abs(c2) = " << abs(c2) << endl; 69 c1 += c2; 70 cout << "c1 += c2, c1 = " << c1 << endl; 71 cout << boolalpha; 72 cout << "c1 == c2 : " << (c1 == c2)<< endl; 73 cout << "c1 != c2 : " << (c1 != c2) << endl; 74 c4 = c2 + c3; 75 cout << "c4 = c2 + c3, c4 = " << c4 << endl; 76 }


#问题1:
比较自定义类Complex和标准库模板类complex的用法,在使用形式上,哪一种更简洁?函数和运算内在有关联吗?
标准库模板类complex。
自定义类Complex使用的时成员函数和普通函数,函数名称一珂表达意图,但与数学符号的自然表达方式不符,内在关联较弱;标准库complex使用的是运算符重载,这些运算符与数学中的运算符号对应,内在关联更强。
##问题2:
2-1:自定义Complex中, output/abs/add/ 等均设为友元,它们真的需要访问 私有数据 吗?(回答“是/否”并给出理由)
否;这些函数可以通过类的公用接口(get_real(),get_imag())来实现,不需要直接访问私有数据。
2-2:标准库 std :complex 是否把 abs 设为友元?(查阅 cppreference后回答)
否;std::abs是非成员函数,不是友元函数。
2-3:什么时候才考虑使用 friend?总结你的思考。
函数无法通过公用接口完成操作,必须直接访问私有成员时;
两个类之间操作频繁时;
###问题3:
如果构造对象时禁用=形式,即遇到Complex c4 = c2;编译报错,类Complex的设计应如何调整?
可以在复制构造函数前加关键字explicit修饰,即explicit Complex(const Complex &c);
explicit会阻止编译器进行隐式转换和拷贝初始化。
任务3:
PlayerControl.h
1 #pragma once 2 #include<string> 3 4 enum class ControlType{Play,Pause,Next,Prev,Stop,Unknown}; 5 6 class PlayerControl { 7 public: 8 PlayerControl(); 9 //实现std::string --> ControlType转换 10 ControlType parse(const std::string & control_str); 11 //执行控制操作(以打印输出模拟) 12 void execute(ControlType cmd) const; 13 14 static int get_cnt(); 15 16 private: 17 static int total_cnt; 18 };
PlayerControl.cpp
1 #include "PlayerControl.h" 2 #include<iostream> 3 #include<algorithm> 4 5 int PlayerControl::total_cnt = 0; 6 7 PlayerControl::PlayerControl() {}; 8 9 //实现std::string --> ControlType转换 10 ControlType PlayerControl::parse(const std::string & control_str) { 11 PlayerControl::total_cnt++; 12 13 std::string control{control_str}; 14 for(auto &i:control) 15 i = std::tolower(i); 16 17 if(control == "play") return ControlType::Play; 18 else if(control == "pause") return ControlType::Pause; 19 else if(control == "next") return ControlType::Next; 20 else if(control == "prev") return ControlType::Prev; 21 else if(control == "stop") return ControlType::Stop; 22 else return ControlType::Unknown; 23 24 } 25 26 void PlayerControl::execute(ControlType cmd) const { 27 switch(cmd) { 28 case ControlType::Play: std::cout << "[Play] Playing music...\n"; break; 29 case ControlType::Pause: std::cout << "[Pause] Music paused\n"; break; 30 case ControlType::Next: std::cout << "[Next] Skipping to next track\n"; break; 31 case ControlType::Prev: std::cout << "[Prev] Back to previous track\n"; break; 32 case ControlType::Stop: std::cout << "[Stop] Music stopped\n"; break; 33 default: std::cout << "[Error] Unknown control\n"; break; 34 } 35 } 36 37 int PlayerControl::get_cnt() { return total_cnt; }
task3.cpp
1 #include "PlayerControl.h" 2 #include <iostream> 3 4 void test() { 5 PlayerControl controller; 6 std::string control_str; 7 std::cout << "Enter Control:(play/pause/next/prev/stop/quit):\n"; 8 9 while(std::cin >> control_str) { 10 if(control_str == "quit") 11 break; 12 13 ControlType cmd = controller.parse(control_str); 14 controller.execute(cmd); 15 std::cout << "Current Player control:" << PlayerControl::get_cnt() << "\n\n"; 16 } 17 } 18 19 int main() { 20 test(); 21 }

#思考:
如果希望输模拟播放控制时,输出控制更现代(使用emoji),如下测试截图所示。如何调整代码实现?
可以引入专门的emoji库:#include<emojicpp/emojicpp.h>
任务4:
Fraction.h
1 #pragma once 2 #include<iostream> 3 #include<string> 4 5 class Fraction { 6 //类的属性 7 public: 8 static std::string doc; 9 private: 10 int up,down; 11 public: 12 static void output(const Fraction &f); 13 //对象方法 14 public: 15 Fraction(int x = 1,int y = 1); 16 Fraction(const Fraction &f); 17 18 int get_up() const; 19 int get_down() const; 20 Fraction negative() const; 21 22 friend Fraction add(const Fraction &f1,const Fraction &f2); 23 friend Fraction sub(const Fraction &f1,const Fraction &f2); 24 friend Fraction mul(const Fraction &f1,const Fraction &f2); 25 friend Fraction div(const Fraction &f1,const Fraction &f2); 26 27 }; 28 29 Fraction add(const Fraction &f1,const Fraction &f2); 30 Fraction sub(const Fraction &f1,const Fraction &f2); 31 Fraction mul(const Fraction &f1,const Fraction &f2); 32 Fraction div(const Fraction &f1,const Fraction &f2); 33 34 int symbol_adjust(int x);
Fraction.cpp
1 #include "Fraction.h" 2 #include<iostream> 3 #include<string> 4 #include<cmath> 5 6 std::string Fraction::doc{"Fraction类 v 0.01版.\\n目前仅支持分数对象的构造、输出、加/减/乘/除运算"}; 7 8 int symbol_adjust(int x) { 9 if(x < 0) return -1; 10 else return 1; 11 } 12 13 void Fraction::output(const Fraction &f) { 14 if(f.down == 0) { 15 std::cout << "分母不能为0"; 16 return; 17 } 18 if(f.up == 0) { 19 std::cout << "0"; 20 return; 21 } 22 if(f.down == 1){ 23 std::cout << f.up; 24 return; 25 } 26 std::cout << f.up << "/" << f.down; 27 } 28 29 Fraction::Fraction(int x,int y):up{abs(x)},down{abs(y)} { 30 int temp,symbol; 31 if(up != 0 && down != 0) { 32 if(up < down) { 33 temp = up; 34 up = down; 35 down = temp; 36 } 37 while(up % down != 0) { 38 temp = up % down; 39 up = down; 40 down = temp; 41 } 42 temp = down; 43 44 symbol = symbol_adjust(x) * symbol_adjust(y); 45 up = symbol * abs(x) / temp; 46 down = abs(y) / temp; 47 } 48 } 49 Fraction::Fraction(const Fraction &f):up{f.up},down{f.down} {} 50 51 int Fraction::get_up() const { return up; } 52 int Fraction::get_down() const { return down; } 53 Fraction Fraction::negative() const { return Fraction(-up,down); } 54 55 Fraction add(const Fraction &f1,const Fraction &f2) { 56 int up = f1.up * f2.down + f1.down * f2.up; 57 int down = f1.down * f2.down; 58 return Fraction(up,down); 59 } 60 61 Fraction sub(const Fraction &f1,const Fraction &f2) { 62 int up = f1.up * f2.down - f1.down * f2.up; 63 int down = f1.down * f2.down; 64 return Fraction(up,down); 65 } 66 67 Fraction mul(const Fraction &f1,const Fraction &f2) { 68 int up = f1.up * f2.up; 69 int down = f1.down * f2.down; 70 return Fraction(up,down); 71 } 72 73 Fraction div(const Fraction &f1,const Fraction &f2) { 74 int up = f1.up * f2.down; 75 int down = f1.down * f2.up; 76 return Fraction(up,down); 77 }
task4.cpp
1 #include "Fraction.h" 2 #include <iostream> 3 4 void test1(); 5 void test2(); 6 7 int main() { 8 std::cout << "测试1: Fraction类基础功能测试\n"; 9 test1(); 10 std::cout << "\n测试2: 分母为0测试: \n"; 11 test2(); 12 } 13 14 void test1() { 15 using std::cout; 16 using std::endl; 17 18 cout << "Fraction类测试: " << endl; 19 cout << Fraction::doc << endl << endl; 20 21 Fraction f1(5); 22 Fraction f2(3, -4), f3(-18, 12); 23 Fraction f4(f3); 24 25 cout << "f1 = "; Fraction::output(f1); cout << endl; 26 cout << "f2 = "; Fraction::output(f2); cout << endl; 27 cout << "f3 = "; Fraction::output(f3); cout << endl; 28 cout << "f4 = "; Fraction::output(f4); cout << endl; 29 30 const Fraction f5(f4.negative()); 31 cout << "f5 = "; Fraction::output(f5); cout << endl; 32 cout << "f5.get_up() = " << f5.get_up() 33 << ", f5.get_down() = " << f5.get_down() << endl; 34 35 cout << "f1 + f2 = "; Fraction::output(add(f1, f2)); cout << endl; 36 cout << "f1 - f2 = "; Fraction::output(sub(f1, f2)); cout << endl; 37 cout << "f1 * f2 = "; Fraction::output(mul(f1, f2)); cout << endl; 38 cout << "f1 / f2 = "; Fraction::output(div(f1, f2)); cout << endl; 39 cout << "f4 + f5 = "; Fraction::output(add(f4, f5)); cout << endl; 40 } 41 42 void test2() { 43 using std::cout; 44 using std::endl; 45 46 Fraction f6(42, 55), f7(0, 3); 47 cout << "f6 = "; Fraction::output(f6); cout << endl; 48 cout << "f7 = "; Fraction::output(f7); cout << endl; 49 cout << "f6 / f7 = "; Fraction::output(div(f6, f7)); cout << endl; 50 }

#问题回答:
分数的输出和计算, output/add/sub/mul/div ,你选择的是哪一种设计方案?(友元/自由函数/命名空间+自由函数/类+static) 你的决策理由?如友元方案的优缺点、静态成员函数方案的适用场景、命名空间方案的考虑因素等。
output选用类加+static:Fraction::output(f)的调用形式清晰表明这是关于Fraction类的功能。
其余均选用友元函数:友元函数访问私有元素更加便利,支持对称性操作。
实验总结:通过本次实验,我学会了创建C++项目的流程,学会了当代码过长时,如何构建头文件,采用多文件结构解决问题。
浙公网安备 33010602011771号