实验二
任务一:
T.h
1 #pragma once 2 #include<string> 3 class T 4 { 5 public: 6 T(int x=0,int y=0); 7 T(const T &t); 8 T(T &&t); 9 ~T(); 10 void adjust(int ratio); 11 void display() const; 12 private: 13 int m1,m2; 14 public: 15 static int get_cnt(); 16 public: 17 static const std::string doc; 18 static const int max_cnt; 19 private: 20 static int cnt; 21 friend void func() ; 22 }; 23 void func();
T.cpp
#include"T.h" #include<iostream> #include<string> 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<<"tset 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 object'max count: "<<T::max_cnt<<endl; cout<<"T object'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 object'current count: "<<T::get_cnt()<<endl; }
运行结果截图

(1) T.h中,在类T内部,已声明 行。 func 是T的友元函数。在类外部,去掉line36,重新编译,程序能否正常运 如果能,回答YES;如果不能,以截图形式提供编译报错信息,说明原因。
答:不能,因为虽然友元函数声明在类内部,但这只是告诉编译器该函数可以访问类的私有成员,友元函数本身是全局函数,需要在类外部有独立的声明或定义
报错截图

(2) T.h中,line9-12给出了各种构造函数、析构函数。总结它们各自的功能、调用时机。
答:T(int x=0, int y=0);创建T类对象时初始化数据成员,两个参数都有默认值,可以接受0个、1个或2个参数,执行对象的初始化工作。调用时机:创建新对象时
T(const T &t);用同类型的另一个对象来初始化新对象,const引用参数确保不会修改原对象。调用时机:对象复制时
T(T &&t);从临时对象或即将销毁的对象"窃取"资源,参数是非常量右值引用。调用时机:临时对象移动时
~T();对象生命周期结束时清理资源。调用时机:对象销毁时
(3)T.cpp中,line13-15,剪切到T.h的末尾,重新编译,程序能否正确编译。 如不能,以截图形式给出报错信息,分析原因。
答:不能编译。因为成员定义放在头文件中后,cpp文件包含头文件导致多次被定义。
报错截图

任务二
Complex.h
1 #pragma once 2 #include<string> 3 class Complex 4 { 5 public: 6 Complex(double x=0,double y=0) ; 7 Complex(const Complex &c); 8 Complex(Complex &&c); 9 Complex& operator=(const Complex& other); 10 Complex& operator=(Complex&& other) ; 11 ~Complex(); 12 double get_real() const; 13 double get_imag() const; 14 void add(const Complex& other); 15 private: 16 double real,imag; 17 public: 18 static const std::string doc; 19 20 friend void output(const Complex& c); 21 friend double abs(const Complex& c); 22 friend Complex add(const Complex& c1,const Complex& c2); 23 friend bool is_equal(const Complex& c1,const Complex& c2); 24 friend bool is_not_equal(const Complex& c1,const Complex& c2); 25 }; 26 void output(); 27 double abs(const Complex& c); 28 Complex add(const Complex& c1,const Complex& c2); 29 bool is_equal(const Complex& c1,const Complex& c2); 30 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 const std::string Complex::doc{"a simple class sample"}; 6 Complex::Complex(double x,double y) :real{x},imag{y} 7 { 8 } 9 Complex::Complex(const Complex& c):real{c.real},imag{c.imag} 10 { 11 } 12 Complex::Complex(Complex&& c):real{c.real},imag{c.imag} 13 { 14 } 15 Complex &Complex::operator=(const Complex& other) 16 { 17 if(this!=&other) 18 { 19 real=other.real; 20 imag=other.imag; 21 } 22 return *this; 23 } 24 Complex& Complex::operator=(Complex&& other) 25 { 26 if (this != &other) { 27 real = other.real; 28 imag = other.imag; 29 other.real = 0; 30 other.imag = 0; 31 } 32 return *this; 33 } 34 Complex::~Complex() 35 { 36 } 37 double Complex:: get_real() const 38 { 39 return real; 40 } 41 double Complex::get_imag() const 42 { 43 return imag; 44 } 45 void Complex:: add(const Complex& other) 46 { 47 real+=other.real; 48 imag+=other.imag; 49 } 50 void output(const Complex&c) 51 { 52 if(c.imag>=0) 53 std::cout<<c.real<<" + "<<c.imag<<"i"; 54 else 55 std::cout<<c.real<<" - "<<-c.imag<<"i"; 56 } 57 double abs(const Complex&c) 58 { 59 return sqrt(c.real*c.real+c.imag*c.imag); 60 } 61 Complex add(const Complex&c1,const Complex&c2) 62 { 63 return Complex(c1.real+c2.real,c1.imag+c2.imag); 64 } 65 bool is_equal(const Complex& c1,const Complex& c2) 66 { 67 if((c1.real==c2.real)&&(c1.imag==c2.imag)) 68 return true; 69 else 70 return false; 71 } 72 bool is_not_equal(const Complex& c1,const Complex& c2) 73 { 74 return !is_equal(c1,c2); 75 }
task2.cpp
1 #include<iostream> 2 #include<iomanip> 3 #include<complex> 4 #include"Complex.h" 5 void test_Complex(); 6 void test_std_complex(); 7 int main() 8 { 9 std::cout<<"*******测试1: 自定义类Complex*******\n"; 10 test_Complex(); 11 std::cout<<"*******测试2: 标准库模板类Complex*******\n"; 12 test_std_complex(); 13 } 14 void test_Complex() 15 { 16 using std::cout; 17 using std::endl;; 18 using std::boolalpha; 19 cout<< "类成员测试: " << endl; 20 cout<< Complex::doc << endl << endl; 21 cout<<"Complex对象测试: "<<endl; 22 Complex c1; 23 Complex c2(3,-4); 24 Complex c3(c2); 25 Complex c4=c2; 26 const Complex c5(3.5); 27 cout<<"c1 = "; 28 output(c1); 29 cout<<endl; 30 cout<<"c2 = "; 31 output(c2); 32 cout<<endl; 33 cout<<"c3 = "; 34 output(c3); 35 cout<<endl; 36 cout<<"c4 = "; 37 output(c4); 38 cout<<endl; 39 cout<<"c5.real = "<<c5.get_real()<<",c5.imag = "<<c5.get_imag()<<endl<<endl; 40 cout<<"复数运算测试:"<<endl; 41 cout<<"abs(c2) = "<<abs(c2)<<endl; 42 c1.add(c2); 43 cout<<"c1+=c2, c1= "; 44 output(c1); 45 cout<<endl; 46 cout<<boolalpha; 47 cout<<"c1 == c2 : "<<is_equal(c1,c2)<<endl; 48 cout<<"c1 != c2 : "<<is_not_equal(c1,c2)<<endl; 49 c4=add(c2,c3); 50 cout<<"c4 = c2 + c3, c4 = "; 51 output(c4); 52 cout<<endl; 53 } 54 void test_std_complex() 55 { 56 using std::cout; 57 using std::endl; 58 using std::boolalpha; 59 cout<<"std::complex<double>对象测试: "<<endl; 60 std::complex<double>c1; 61 std::complex<double>c2(3,-4); 62 std::complex<double>c3(c2); 63 std::complex<double>c4=c2; 64 const std::complex<double>c5(3.5); 65 cout<<"c1 = "<<c1<<endl; 66 cout<<"c2 = "<<c2<<endl; 67 cout<<"c3 = "<<c3<<endl; 68 cout<<"c4 = "<<c4<<endl; 69 cout<<"c5.real = "<<c5.real()<<", c5.imag = "<<c5.imag()<<endl<<endl; 70 cout<<"复数运算测试: "<<endl; 71 cout<<"abs(c2) = "<<abs(c2)<<endl; 72 c1+=c2; 73 cout<<"c1 += c2, c1 = "<<c1<<endl; 74 cout<<boolalpha; 75 cout<<"c1 == c2 : "<<(c1 ==c2)<<endl; 76 cout<<"c1 != c2 : "<<(c1 !=c2)<<endl; 77 c4=c2+c3; 78 cout<<"c4 = c2 + c3, c4 = "<<c4<<endl; 79 }
运行结果截图


(1)比较自定义类Complex 和标准库模板类complex 的用法,在使用形式上,哪一种更简洁?函数和运算内在有关联吗?
答:标准库模板类complex 在使用形式上更简洁。加法、相等判断、输出等都是相同的数学操作,只是表达形式不同标准库使用运算符重载,语法更简洁,自定义类使用显式函数调用。
(2)1.自定义Complex 中, output/abs/add/ 等均设为友元,它们真的需要访问 私有数据 吗?(回答“是/否”并给出理由)
答:否。因为可以通过公共接口double get_real() const;double get_imag() const;进行访问,更好保持封装性。
2.标准库 std::complex 是否把 abs 设为友元?
答:没有
3.什么时候才考虑使用 friend?总结你的思考。
答:无法通过公共接口访问私有数据,需要避免函数调用时间。
(3)如果构造对象时禁用=形式,即遇到Complex c4 = c2;编译报错,类Complex的设计应如何调整?
答:如果要禁用可以改成Complex(const Complex&) = delete;
任务三
PlayerControl.h
1 #pragma once 2 #include<string> 3 enum class ControlType{Play,Pause,Next,Prev,Stop,Unknown}; 4 class PlayerControl{ 5 public: 6 PlayerControl(); 7 ControlType parase(const std::string& control_str); 8 void execute(ControlType cmd) const; 9 static int get_cnt(); 10 private: 11 static int total_cnt; 12 };
PayerControl.cpp
#include"PlayerControl.h" #include<iostream> #include<algorithm> int PlayerControl::total_cnt=0; PlayerControl::PlayerControl() { } ControlType PlayerControl::parase(const std::string& control_str) { std::string result{control_str}; for (char &i:result ) { i=std::tolower(i); } if(result=="play") { total_cnt++; return ControlType::Play;} else if(result=="pause") { total_cnt++; return ControlType::Pause; } else if(result=="next") { total_cnt++; return ControlType::Next;} else if(result=="prev") { total_cnt++; return ControlType::Prev; } else if(result=="stop") { total_cnt++; return ControlType::Stop;} else return 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
1 #include"PlayerControl.h" 2 #include<iostream> 3 void test() 4 { 5 PlayerControl controller; 6 std::string control_str; 7 std::cout<<"Enter Control: (play/pause/next/prev/stop/quit):\n"; 8 while(std::cin>>control_str) 9 { 10 if(control_str=="quit") 11 break; 12 13 ControlType cmd=controller.parase(control_str); 14 controller.execute(cmd); 15 std::cout<<"Current Player control: "<<PlayerControl::get_cnt()<<"\n\n"; 16 } 17 } 18 int main() 19 { 20 test(); 21 }
运行结果截图

任务四
Fraction.h
1 #pragma once 2 #include<string> 3 namespace Fracction 4 { 5 class Fraction 6 { 7 public: 8 Fraction(int x,int y=1); 9 Fraction(const Fraction &f); 10 ~Fraction(); 11 int get_up() const; 12 int get_down()const; 13 Fraction negative() const; 14 private: 15 int up,down; 16 public: 17 static const std::string doc; 18 }; 19 void output(const Fraction &f); 20 Fraction add(const Fraction &f1,const Fraction &f2); 21 Fraction sub(const Fraction &f1,const Fraction &f2); 22 Fraction mul(const Fraction &f1,const Fraction &f2); 23 Fraction div(const Fraction &f1,const Fraction &f2); 24 int gcd(int a,int b) ; 25 }
Fraction.cpp
1 #include"Fraction.h" 2 #include<iostream> 3 #include<string> 4 const std::string Fraction:: doc{"Fraction类 v 0.01版.\n目前仅支持分数对象的构造、输出、加/减/乘/除运算."}; 5 Fraction::Fraction(int x,int y):up{x},down{y} 6 { 7 } 8 Fraction::Fraction(const Fraction &f):up{f.up},down{f.down} 9 { 10 } 11 int gcd(int a,int b) 12 { 13 if(a<0) 14 a=-a; 15 if(b<0) 16 b=-b; 17 int temp; 18 if(a<b) 19 { 20 temp=a; 21 a=b; 22 b=temp; 23 } 24 while(b!=0) 25 { 26 temp=b; 27 b=a%b; 28 a=temp; 29 } 30 return a; 31 } 32 int Fraction::get_up() const 33 { 34 int m=gcd(up,down); 35 return up/m; 36 } 37 int Fraction::get_down() const 38 { 39 int m=gcd(up,down); 40 return down/m; 41 } 42 Fraction Fraction::negative() const 43 { 44 int num=up; 45 int den=down; 46 if(num<0&&den<0) 47 { 48 den=-den; 49 return Fraction(num,den); 50 } 51 if(num>0&&den>0) 52 { 53 num=-num; 54 return Fraction(num,den); 55 } 56 if(num<0&&den>0) 57 { 58 num=-num; 59 return Fraction(num,den); 60 } 61 if(num>0&&den<0) 62 { 63 den=-den; 64 return Fraction(num,den); 65 } 66 } 67 Fraction::~Fraction() 68 { 69 70 } 71 72 void output(const Fraction &f) 73 { 74 int num = f.get_up(); 75 int den = f.get_down(); 76 if(num<0&&den<0) 77 { 78 num=-num; 79 den=-den; 80 } 81 if(num>0&&den<0) 82 { 83 num=-num; 84 den=-den; 85 } 86 int m=gcd(num,den); 87 if(den!=0&&f.get_down()!=1&&num!=0) 88 { 89 90 std::cout<<num/m<<"/"<<den/m; 91 } 92 else if(den==0) 93 std::cout<<"分母不能为0"; 94 else if(den==1&&num!=0) 95 std::cout<<num; 96 else if(den!=0&&num==0) 97 std::cout<<"0"; 98 } 99 Fraction add(const Fraction &f1,const Fraction &f2) 100 { 101 int num1 = f1.get_up(); 102 int den1 = f1.get_down(); 103 int num2 = f2.get_up(); 104 int den2 = f2.get_down(); 105 return Fraction(num1*den2+num2*den1,den1*den2); 106 } 107 Fraction sub(const Fraction &f1,const Fraction &f2) 108 { 109 int num1 = f1.get_up(); 110 int den1 = f1.get_down(); 111 int num2 = f2.get_up(); 112 int den2 = f2.get_down(); 113 return Fraction(num1*den2-num2*den1,den1*den2); 114 } 115 Fraction mul(const Fraction &f1,const Fraction &f2) 116 { 117 int num1 = f1.get_up(); 118 int den1 = f1.get_down(); 119 int num2 = f2.get_up(); 120 int den2 = f2.get_down(); 121 return Fraction(num1*num2,den1*den2); 122 } 123 Fraction div(const Fraction &f1,const Fraction &f2) 124 { 125 int num1 = f1.get_up(); 126 int den1 = f1.get_down(); 127 int num2 = f2.get_up(); 128 int den2 = f2.get_down(); 129 return Fraction(num1*den2,num2*den1); 130 }
task4.cpp
1 #include"Fraction.h" 2 #include<iostream> 3 void test1(); 4 void test2(); 5 int main() 6 { 7 std::cout<<"测试1: Fraction类基础功能测试\n"; 8 test1(); 9 std::cout << "\n测试2: 分母为0测试: \n"; 10 test2(); 11 } 12 void test1() 13 { 14 using std::cout; 15 using std::endl; 16 cout << "Fraction类测试: " << endl; 17 cout << Fraction::doc << endl << endl; 18 Fraction f1(5); 19 Fraction f2(3, -4), f3(-18, 12); 20 Fraction f4(f3); 21 cout << "f1 = "; output(f1); cout << endl; 22 cout << "f2 = "; output(f2); cout << endl; 23 cout << "f3 = "; output(f3); cout << endl; 24 cout << "f4 = "; output(f4); cout << endl; 25 const Fraction f5(f4.negative()); 26 cout << "f5 = "; output(f5); cout << endl; 27 cout << "f5.get_up() = " << f5.get_up() << ", f5.get_down() = " << f5.get_down() << endl; 28 cout << "f1 + f2 = "; output(add(f1, f2)); cout << endl; 29 cout << "f1 - f2 = "; output(sub(f1, f2)); cout << endl; 30 cout << "f1 * f2 = "; output(mul(f1, f2)); cout << endl; 31 cout << "f1 / f2 = "; output(div(f1, f2)); cout << endl; 32 cout << "f4 + f5 = "; output(add(f4, f5)); cout << endl; 33 } 34 void test2() 35 { 36 using std::cout; 37 using std::endl; 38 Fraction f6(42, 55), f7(0, 3); 39 cout << "f6 = "; output(f6); cout << endl; 40 cout << "f7 = "; output(f7); cout << endl; 41 cout << "f6 / f7 = "; output(div(f6, f7)); cout << endl; 42 } 43
运行结果截图

(1)分数的输出和计算, output/add/sub/mul/div你选择的是哪一种设计方案?(友元/自由函数/命名空间+自由函数/类+static)
答:选择的命名空间+自由函数。
(2)你的决策理由?如友元方案的优缺点、静态成员函数方案的适用场景、命名空间方案的考虑因素等
答:相比于单单使用自由函数,可以避免命名冲突。友元方案的评估虽然直接访问私有成员操作类的内部状态破坏封装性,外部函数可以操作类的私有数,没必要直接访问私有成员可以用公共接口。静态成员函数方案的适用场景:操作与类紧密相关且不需要对象实例,函数逻辑主要依赖于类的内部实现,需要访问私有构造函数,但是output/add/sub/mul/div本质上是算法操作,不是类的核心职责,会使类接口变得臃肿.
总结与体会:非常量引用(Fraction &f)只能绑定到左值(即具有名称的对象,可以被取地址),不能绑定到临时对象(右值)或常量对象。常量引用(const Fraction &f)可以绑定到左值、右值(临时对象)和常量对象。函数定义应该在cpp文件中定义,在头文件里会有重复定义的报错。友元函数要合理使用,有公共接口访问便利时,可以不使用,保持封装性。

浙公网安备 33010602011771号