oop-实验2
task1
T.h
1 #pragma 2 3 #include<string> 4 5 class T{ 6 public: 7 T(int x=0,int y=0); 8 T(const T &t); 9 T(T &&t); 10 ~T(); 11 12 void adjust(int ratio); //按系数成倍地调整数据 13 void display() const; //以(m1,m2)形式调整数据 14 15 private: 16 int m1,m2; //坐标 17 18 //类属性及方法 19 public: 20 static int get_cnt(); //显示当前类T对象总数 21 public: 22 static const std::string doc; //类T的描述信息 23 static const int max_cnt; //类T对象上限 24 25 private: 26 static int cnt; //显示T类对象总数 27 28 friend void func(); //需要两种声明,这里是friend访问权限的声明 29 }; 30 31 //这个是函数本身的声明 32 void func();
T.cpp
1 #include"T.h" 2 #include<iostream> 3 #include<string> 4 5 //类T实现 6 7 //static成员数据类外初始化,不需要加static 8 const std::string T::doc{"a simple class sample"}; 9 const int T::max_cnt=999; 10 int T::cnt=0; 11 12 int T::get_cnt(){ 13 return cnt; 14 } 15 16 T::T(int x,int y):m1(x),m2(y){ 17 cnt++; 18 std::cout<<"T constructor called.\n"; 19 } 20 21 T::T(const T &t):m1(t.m1),m2(t.m2){ 22 cnt++; 23 std::cout<<"T copy constructor called.\n"; 24 } 25 26 T::T(T &&t):m1(t.m1),m2(t.m2){ 27 cnt++; 28 std::cout<<"T move constructor called.\n"; 29 } 30 31 T::~T(){ 32 cnt--; 33 std::cout<<"T destructor called.\n"; 34 } 35 36 void T::adjust(int ratio){ 37 m1*=ratio; 38 m2*=ratio; 39 } 40 41 void T::display() const{ 42 std::cout<<"("<<m1<<","<<m2<<")"; 43 } 44 45 46 //普通函数实现 47 void func(){ 48 T t5(42); 49 t5.m2=2049; 50 std::cout<<"t5="; 51 t5.display(); 52 std::cout<<"\n"; 53 }
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 std::cout<<"\ntest friend func: \n"; 10 func(); 11 } 12 13 void test_T(){ 14 using std::cout; 15 using std::endl; 16 cout<<"T info:"<<T::doc<<endl; 17 cout<<"T objects' max count :"<<T::max_cnt<<endl; 18 cout<<"T objects' current count :"<<T::get_cnt()<<endl<<endl; 19 20 T t1; 21 cout<<"t1="; t1.display(); cout<<endl; 22 23 T t2(3,4); 24 cout<<"t2="; t2.display(); cout<<endl; 25 26 T t3(t2); 27 t3.adjust(2); 28 cout<<"t3="; t3.display(); cout<<endl; 29 30 T t4(std::move(t2)); 31 cout<<"t4="; t4.display(); cout<<endl; 32 33 cout<<"test: T objects'current count: "<<T::get_cnt()<<endl; 34 }
test result:

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

错误显示函数没有被声明,friend void func()只是声明它是类T的友元函数,并不是这个函数本身的声明,还需要在类外单独进行函数的声明
问题2:T.h中,line9-12给出了各种构造函数、析构函数。总结它们各自的功能、调用时机。
T(int x=0,int y=0); 这个是普通构造函数,当传入的参数是int类型值进行成员变量的初始化,如果没有传入的话,成员变量就是默认参数的值。
T(const T &t); 复制构造函数,当传入的参数是一个左值引用对象时,复制对象的成员数据,创建一个新的对象。
T(T &&t); 移动构造函数,当传入的参数是一个右值引用对象时,将这个右值对象的资源给新对象接管。
~T(); 析构函数,在对象即将毁灭时编译器自动调用。
问题3:T.cpp中,line13-15,剪切到T.h的末尾,重新编译,程序能否正确编译。如不能,以截图形式给出报错信息,分析原因。
报错信息:

因为源文件中static变量的定义放在头文件中会导致重复定义的问题,应该加上关键字inline。
task2
Complex.h
1 #pragma 2 3 #include<string> 4 5 class Complex{ 6 //类属性 7 public: 8 static const std::string doc; 9 10 //对象属性 11 private: 12 double real,imag; 13 14 public: 15 Complex(double x=0,double y=0); 16 Complex(const Complex &c)=default; 17 Complex(Complex &&c)=default; 18 Complex& operator=(const Complex&) = default; 19 Complex& operator=(Complex&&) = default; 20 21 double get_real() const; 22 double get_imag() const; 23 void add(const Complex &c); 24 25 //友元函数 26 friend void output(const Complex &c); 27 friend double abs(const Complex &c); 28 friend Complex add(const Complex &c1,const Complex &c2); 29 friend bool is_equal(const Complex &c1,const Complex &c2); 30 friend bool is_not_equal(const Complex &c1,const Complex &c2); 31 }; 32 33 inline const std::string Complex::doc="a simplified complex class";
Complex.cpp
1 #include"Complex.h" 2 #include<iostream> 3 #include<cmath> 4 5 Complex::Complex(double x,double y):real(x),imag(y){} 6 7 double Complex::get_real() const{ return real;} 8 9 double Complex::get_imag() const { return imag;} 10 11 void Complex::add(const Complex &c){ 12 real+=c.real; 13 imag+=c.imag; 14 } 15 16 void output(const Complex &c){ 17 if(c.imag<0){ 18 std::cout<<c.real<<c.imag<<"i"; 19 return; 20 } 21 std::cout<<c.real<<"+"<<c.imag<<"i"; 22 } 23 24 double abs(const Complex &c){ 25 return sqrt(c.real*c.real+c.imag*c.imag); 26 } 27 28 Complex add(const Complex &c1,const Complex &c2){ 29 return Complex(c1.real+c2.real,c2.imag+c1.imag); 30 } 31 32 bool is_equal(const Complex &c1,const Complex &c2){ 33 if(c1.real==c2.real&&c1.imag==c2.imag) return true; 34 return false; 35 } 36 37 bool is_not_equal(const Complex &c1,const Complex &c2){ 38 return is_equal(c1,c2)?false:true; 39 }
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 13 std::cout<<"测试2:std::complex\n"; 14 test_std_complex(); 15 } 16 17 void test_Complex() { 18 using std::cout; 19 using std::endl; 20 using std::boolalpha; 21 cout << "类成员测试: " << endl; 22 cout << Complex::doc << endl << endl; 23 cout << "Complex对象测试: " << endl; 24 Complex c1; 25 Complex c2(3, -4); 26 Complex c3(c2); 27 Complex c4 = c2; 28 const Complex c5(3.5); 29 cout << "c1 = "; output(c1); cout << endl; 30 cout << "c2 = "; output(c2); cout << endl; 31 cout << "c3 = "; output(c3); cout << endl; 32 cout << "c4 = "; output(c4); cout << endl; 33 cout << "c5.real = " << c5.get_real()<< ", c5.imag = " << c5.get_imag() << endl << endl; 34 cout << "复数运算测试: " << endl; 35 cout << "abs(c2) = " << abs(c2) << endl; 36 c1.add(c2); 37 cout << "c1 += c2, c1 = "; output(c1); cout << endl; 38 cout << boolalpha; 39 cout << "c1 == c2 : " << is_equal(c1, c2) << endl; 40 cout << "c1 != c2 : " << is_not_equal(c1, c2) << endl; 41 c4 = add(c2, c3); 42 cout << "c4 = c2 + c3, c4 = "; output(c4); cout << endl; 43 } 44 45 void test_std_complex() { 46 using std::cout; 47 using std::endl; 48 using std::boolalpha; 49 cout << "std::complex<double>对象测试: " << endl; 50 std::complex<double> c1; 51 std::complex<double> c2(3, -4); 52 std::complex<double> c3(c2); 53 std::complex<double> c4 = c2; 54 const std::complex<double> c5(3.5); 55 cout << "c1 = " << c1 << endl; 56 cout << "c2 = " << c2 << endl; 57 cout << "c3 = " << c3 << endl; 58 cout << "c4 = " << c4 << endl; 59 cout << "c5.real = " << c5.real()<< ", c5.imag = " << c5.imag() << endl << endl; 60 cout << "复数运算测试: " << endl; 61 cout << "abs(c2) = " << abs(c2) << endl; 62 c1 += c2; 63 cout << "c1 += c2, c1 = " << c1 << endl; 64 cout << boolalpha; 65 cout << "c1 == c2 : " << (c1 == c2)<< endl; 66 cout << "c1 != c2 : " << (c1 != c2) << endl; 67 c4 = c2 + c3; 68 cout << "c4 = c2 + c3, c4 = " << c4 << endl; 69 }
test result


问题1:比较自定义类 Complex 和标准库模板类 complex 的用法,在使用形式上,哪一种更简洁?函数和运算内在有关联吗?
std::complex更简洁,函数和运算内在有关联,就是根据函数作用重载运算符+等,使用起来更符和数学直觉,也方便我们理解和使用。
问题2:
2-1:自定义 Complex 中, output/abs/add/ 等均设为友元,它们真的需要访问 私有数据 吗?(回答“是/否”并给出理由)
不需要,还有一种方式是调用接口get_real(),get_imag()。
2-2:标准库 std::complex 是否把 abs 设为友元?(查阅 cppreference后回答)
否。
2-3:什么时候才考虑使用 friend?总结你的思考。
需要访问一个类的私有成员数据且类内部并没有提供访问私有成员数据的接口。
但是如果访问频率较高的话使用接口可能导致效率低,声明为友元函数则效率更高。
如果两个类之间操作密集,使用友元函数。
问题3:如果构造对象时禁用=形式,即遇到 Complex c4 = c2; 编译报错,类Complex的设计应如何调整?
加上关键字explicit,防止隐式类型转换。
task3
PlayerControl.h
1 #pragma 2 #include<string> 3 4 enum class ControlType{Play,Pause,Next,Prev,Stop,Unknown}; 5 6 class PlayerControl{ 7 private: 8 static int total_cnt; 9 public: 10 static int get_cnt(); 11 12 public: 13 PlayerControl(); 14 ControlType parse(const std::string& control_str); //实现std::string向ControlType转换 15 void execute(ControlType cmd) const; //执行控制操作(以打印输出模拟) 16 };
PlayerControl.cpp
1 #include"PlayerControl.h" 2 #include<string> 3 #include<iostream> 4 #include<algorithm> 5 6 int PlayerControl::total_cnt=0; 7 8 PlayerControl::PlayerControl(){}; 9 10 ControlType PlayerControl::parse(const std::string &control_str){ 11 std::string str=""; 12 total_cnt++; 13 str.resize(control_str.size()); //transform()需要给str提前预留足够空间 / string str(control_str.size(),' ') 14 std::transform(control_str.begin(),control_str.end(),str.begin(),[](unsigned char c){ return std::tolower(c); }); 15 if(str=="play"){ 16 return ControlType::Play; 17 }else if(str=="pause"){ 18 return ControlType::Pause; 19 }else if(str == "next"){ 20 return ControlType::Next; 21 }else if(str == "prev"){ 22 return ControlType::Prev; 23 }else if(str == "stop"){ 24 return ControlType::Stop; 25 }else{ 26 return ControlType::Unknown; 27 } 28 } 29 30 void PlayerControl::execute(ControlType cmd) const{ 31 switch (cmd) { 32 case ControlType::Play: std::cout << "[play] Playing music...\n"; break; 33 case ControlType::Pause: std::cout << "[Pause] Music paused\n"; break; 34 case ControlType::Next: std::cout << "[Next] Skipping to next track\n"; break; 35 case ControlType::Prev: std::cout << "[Prev] Back to previous track\n"; break; 36 case ControlType::Stop: std::cout << "[Stop] Music stopped\n"; break; 37 default: std::cout << "[Error] unknown control\n"; break; 38 } 39 } 40 41 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") break; 11 ControlType cmd = controller.parse(control_str); 12 controller.execute(cmd); 13 std::cout << "Current Player control: " << PlayerControl::get_cnt() << "\n\n"; 14 } 15 } 16 17 int main(){ test(); }
test result

思考(选做*)
如果希望输模拟播放控制时,输出控制更现代(使用emoji),如下测试截图所示。如何调整代码实现?
在代码中把emoji表情加进去

test result

task4
Fraction.h
1 #pragma 2 3 #include<string> 4 5 class Fraction{ 6 friend class CollectionF; 7 public: 8 static std::string doc; 9 10 private: 11 int up,down; 12 public: 13 Fraction(int up,int down=1); 14 int get_up() const; 15 int get_down() const; 16 Fraction negative() const; //用于求负 17 Fraction operator-() const; 18 }; 19 20 inline std::string Fraction::doc="Fraction类 v 0.01版.\n目前仅支持分数对象的构造、输出、加/减/乘/除运算.";
Fraction.cpp
1 #pragma 2 3 #include"Fraction.h" 4 #include"CollectionF.h" 5 #include<string> 6 #include<iostream> 7 8 int Fraction::get_up() const{ 9 return CollectionF::fun(*this).up; 10 } 11 int Fraction::get_down() const{ 12 return CollectionF::fun(*this).down; 13 } 14 Fraction::Fraction(int up,int down):up(up),down(down){} 15 Fraction Fraction::operator-() const{ 16 return Fraction(-this->up,this->down); 17 } 18 Fraction Fraction::negative() const{ 19 return -(*this); 20 }
CollectionF.h
1 #pragma 2 3 //工具类 4 class CollectionF{ 5 public: 6 static Fraction fun(Fraction f); //化简 7 static void output(Fraction f); 8 static Fraction add(const Fraction &f1,const Fraction &f2); 9 static Fraction sub(const Fraction &f1,const Fraction &f2); 10 static Fraction mul(const Fraction &f1,const Fraction &f2); 11 static Fraction div(const Fraction &f1,const Fraction &f2); 12 }; 13 14 int gcd(int a,int b);
CollectionF.cpp
1 #pragma 2 #include"Fraction.h" 3 #include"CollectionF.h" 4 #include<iostream> 5 #include<cmath> 6 7 int gcd(int a,int b){ 8 a=abs(a); b=abs(b); 9 return (b==0)?a:gcd(b,a%b); 10 } 11 //化简 12 Fraction CollectionF::fun(Fraction f){ 13 if (f.down == 0) return Fraction(0, 0); // 分母为0,返回特殊值 14 int gcdnum = gcd(f.up, f.down); 15 int new_up = f.up / gcdnum; 16 int new_down = f.down / gcdnum; 17 // 规范化符号:将负号转移到分子,保证分母始终为正 18 if (new_down < 0) { 19 new_up = -new_up; 20 new_down = -new_down; 21 } 22 return Fraction(new_up, new_down); 23 24 } 25 void CollectionF::output(Fraction f){ 26 Fraction f0 = fun(f); // 先进行化简 27 if(f0.down==0){std::cout<<"分母不能为0"; return;} 28 if(f0.up==0){std::cout << 0; return;} 29 if(f0.down == 1){std::cout << f0.up; return;} 30 if ((f0.up > 0 && f0.down > 0) || (f0.up < 0 && f0.down < 0)) { 31 //正分数 32 std::cout<<abs(f0.up)<<"/"<<abs(f0.down); 33 }else{ 34 //负分数 35 std::cout<<"-"<<abs(f0.up)<<"/"<<abs(f0.down); 36 } 37 } 38 39 Fraction CollectionF::add(const Fraction &f1,const Fraction &f2){ 40 int newup=f1.up*f2.down+f2.up*f1.down,newdown=f1.down*f2.down; 41 int gcdnum=gcd(newup,newdown); 42 return Fraction(newup/gcdnum,newdown/gcdnum); 43 } 44 Fraction CollectionF::sub(const Fraction &f1,const Fraction &f2){ 45 Fraction f0(-f2); 46 return add(f1,f0); 47 } 48 Fraction CollectionF::mul(const Fraction &f1,const Fraction &f2){ 49 int newup=f1.up*f2.up,newdown=f1.down*f2.down; 50 int gcdnum=gcd(newup,newdown); 51 return Fraction(newup/gcdnum,newdown/gcdnum); 52 } 53 Fraction CollectionF::div(const Fraction &f1,const Fraction &f2){ 54 if(f2.down==0) return Fraction(0,0); 55 Fraction f0(f2.down,f2.up); 56 return mul(f1,f0); 57 }
task4.cpp
#include "Fraction.h" #include"CollectionF.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 = "; CollectionF::output(f1); cout << endl; cout << "f2 = "; CollectionF::output(f2); cout << endl; cout << "f3 = "; CollectionF::output(f3); cout << endl; cout << "f4 = "; CollectionF::output(f3); cout << endl; const Fraction f5(f4.negative()); cout << "f5 = "; CollectionF::output(f5); cout << endl; cout << "f5.get_up() = " << f5.get_up() << ", f5.get_down() = " << f5.get_down() << endl; cout << "f1 + f2 = "; CollectionF::output(CollectionF::add(f1, f2)); cout << endl; cout << "f1 - f2 = "; CollectionF::output(CollectionF::sub(f1, f2)); cout << endl; cout << "f1 * f2 = "; CollectionF::output(CollectionF::mul(f1, f2)); cout << endl; cout << "f1 / f2 = "; CollectionF::output(CollectionF::div(f1, f2)); cout << endl; cout << "f4 + f5 = "; CollectionF::output(CollectionF::add(f4, f5)); cout << endl; } void test2() { using std::cout; using std::endl; Fraction f6(42, 55), f7(0, 3); cout << "f6 = "; CollectionF::output(f6); cout << endl; cout << "f7 = "; CollectionF::output(f7); cout << endl; cout << "f6 / f7 = "; CollectionF::output(CollectionF::div(f6, f7)); cout << endl; }
test result

问题回答:分数的输出和计算, output/add/sub/mul/div ,你选择的是哪一种设计方案?(友元/自由函数/命名空间+自由函数/类+static) 你的决策理由?如友元方案的优缺点、静态成员函数方案的适用场景、命名空间方案的考虑因素等。
我选择的是创建一个工具类,同时将这个工具类设为Fraction的友元类。
因为通过类::static这样调用逻辑清晰,但实际上写完之后代码有些过于冗杂了,通过自由函数的形式更简洁易懂。

浙公网安备 33010602011771号