实验二
试验任务一:
问题一:不能

C++ 编译遵循 “先声明后使用” 原则,即使func在类内被声明为友元函数,也只能给他授予访问权限,而不是在全局域声明函数原型,因此task1.cpp找不到该函数,出现报错。
问题二:
普通构造函数:功能:初始化对象 调用时机:创建对象。
复制构造函数:功能:用已存在对象的成员值初始化新对象 调用时机:用对象初始化新对象,函数参数按值传递对象
移动构造函数:功能:转移右值对象的资源 调用时机:. 用右值初始化新对象。
析构函数:功能:释放对象资源 调用时机: 对象生命周期结束,程序结束时全局 / 静态对象被销毁。
问题三:

类的非inline静态成员变量必须在类外初始化。将T::doc、T::max_cnt、T::cnt的初始化剪切到T.h末尾,仍属于类外初始化。但T.h可能被多个文件包含,导致静态成员变量被重复定义。
试验任务二:
#pragma once #include <string> class Complex { public: static const std::string doc; Complex(); Complex(double real); Complex(double real, double imag); 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; double imag; };
#include "Complex.h" #include <iostream> #include <cmath> #include <string> const std::string Complex::doc = "a simplified complex class"; Complex::Complex() : real(0.0), imag(0.0) {} Complex::Complex(double real) : real(real), imag(0.0) {} 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"; } else if (c.imag < 0) { std::cout << c.real << c.imag << "i"; } else { std::cout << c.real; } } double abs(const Complex& c) { return sqrt(c.real * c.real + c.imag * c.imag); } Complex add(const Complex& c1, const Complex& c2) { Complex a; a.real = c1.real + c2.real; a.imag = c1.imag + c2.imag; return a; } bool is_equal(const Complex& c1, const Complex& c2) { return (c1.real == c2.real) && (c1.imag == c2.imag); } // 友元:判断两复数不等(复用is_equal) bool is_not_equal(const Complex& c1, const Complex& c2) { return !is_equal(c1, c2); }
#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::cout; using std::endl; using std::boolalpha; 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::cout; using std::endl; using std::boolalpha; using std::complex; cout << "std::complex<double>对象测试: " << endl; complex<double> c1; complex<double> c2(3, -4); complex<double> c3(c2); complex<double> c4 = c2; const 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; }
运行结果:

问题一:
标准库模板类更简洁;有关系,如c1.add,c1 += c2。
问题二:
2-1:需要;output需访问real和imag以格式化输出;abs需计算real²+imag²,大家都需要访问real和imag。
2-2:否;std::abs(std::complex<T>)是全局函数,而非友元。std::complex通过公有成员函数real()和imag()对外提供数据访问,避免使用友元,符合 “最小权限原则”。
2-3:需维持类的封装性;无法通过公有成员函数间接获取所需数据;外部函数或类需访问当前类的私有成员。
问题三:
删除复制构造函数即可
Complex(const Complex& other) = delete;
实验任务三:
#pragma once #include <string> enum class ControlType { Play, Pause, Next, Prev, Stop, Unknown }; class PlayerControl { public: PlayerControl(); ControlType parse(const std::string& control_str); void execute(ControlType cmd) const; static int get_cnt(); private: static int total_cnt; };
#include "PlayerControl.h" #include <iostream> #include <algorithm> #include <string> int PlayerControl::total_cnt = 0; PlayerControl::PlayerControl() {} ControlType PlayerControl::parse(const std::string& control_str) { std::string lower_str = control_str; std::transform( lower_str.begin(), lower_str.end(), lower_str.begin(), [](unsigned char c) { return std::tolower(c); }); if (lower_str == "play") { ++total_cnt; return ControlType::Play; } else if (lower_str == "pause") { ++total_cnt; return ControlType::Pause; } else if (lower_str == "next") { ++total_cnt; return ControlType::Next; } else if (lower_str == "prev") { ++total_cnt; return ControlType::Prev; } else if (lower_str == "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; }
#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(); }
运行结果:

实验任务四:
#pragma once #include <string> namespace FractionUtil { class Fraction { public: static const std::string doc; Fraction(int up); Fraction(int up, int down); Fraction(const Fraction& other); int get_up() const; int get_down() const; Fraction negative() const; private: int up; int down; }; 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); }
#include "Fraction.h" #include <iostream> #include <cmath> #include <string> const std::string FractionUtil::Fraction::doc = "Fraction类 v 0.01版.\n目前仅支持分数对象的构造、输出、加 / 减 / 乘 / 除运算."; FractionUtil::Fraction::Fraction(int up) : up(up), down(1) { int a = std::abs(up); int b = std::abs(down); while (b != 0) { int temp = b; b = a % b; a = temp; } int temp = a; up /= temp; down /= temp; } FractionUtil::Fraction::Fraction(int up, int down) : up(up), down(down) { if (down == 0) { std::cout << "错误,分母不能为零\n"; exit(1); } if(down < 0) { up *= -1; down *= -1; } if (up == 0) { down = 1; return; } int a = std::abs(up); int b = std::abs(down); while (b != 0) { int temp = b; b = a % b; a = temp; } int temp = a; up /= temp; down /= temp; } FractionUtil::Fraction::Fraction(const Fraction& other) : up(other.up), down(other.down) {} int FractionUtil::Fraction::get_up() const { return up; } int FractionUtil::Fraction::get_down() const { return down; } FractionUtil::Fraction FractionUtil::Fraction::negative() const { return Fraction(-up, down); } void FractionUtil::output(const Fraction& f) { if (f.get_down() == 1) { std::cout << f.get_up(); } else { std::cout << f.get_up() << "/" << f.get_down(); } } FractionUtil::Fraction FractionUtil::add(const Fraction& f1, const Fraction& f2) { int new_up = f1.get_up() * f2.get_down() + f2.get_up() * f1.get_down(); int new_down = f1.get_down() * f2.get_down(); return Fraction(new_up, new_down); } FractionUtil::Fraction FractionUtil::sub(const Fraction& f1, const Fraction& f2) { int new_up = f1.get_up() * f2.get_down() - f2.get_up() * f1.get_down(); int new_down = f1.get_down() * f2.get_down(); return Fraction(new_up, new_down); } FractionUtil::Fraction FractionUtil::mul(const Fraction& f1, const Fraction& f2) { int new_up = f1.get_up() * f2.get_up(); int new_down = f1.get_down() * f2.get_down(); return Fraction(new_up, new_down); } FractionUtil::Fraction FractionUtil::div(const Fraction& f1, const Fraction& f2) { if (f2.get_up() == 0) { std::cout << "分母不能为"; return NULL; } int new_up = f1.get_up() * f2.get_down(); int new_down = f1.get_down() * f2.get_up(); return Fraction(new_up, new_down); }
#include "Fraction.h" #include <iostream> using namespace FractionUtil; 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::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 << "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; }
运行结果:

选择方案:命名空间+自由函数;
友元的缺点:破坏封装性,如果后续有更改的话修改起来很麻烦;
命名空间 + 自由函数的优势:方便添加新东西;感觉更简单;并且自由函数不依赖类的私有成员。
实验总结:
实验一就是在告诉我们关于友元函数的声明和调用的一些规则,注意即可。
实验二就是强调了什么情况是使用友元函数,本身实验难度不高。
实验三难度主要在将那个。字符串转化成统一大写或小写的字符串,我在这里用了 Transform函数,主要它可以便利的将字符串里的每个字符操作一遍,再通过tolower可以很快的将字符串统一小写,相较于去写循环操作要方便很多。至于其他的内容就是正常的字符匹配。
实验四需要考虑的内容就比较多,主要因为他是分数,在初始化的时候,单参数初始化还好,双参数初始化需要去考虑分母是不是为0,就需要多列一个情况。至于那个 negative,还有加减乘除这四个算法,为了减少代码行数,我就把计算得到的数字全部进行初始化,这样也可以不用考虑约分之类的,因为这些操作都已经在初始化函数里面。但写完我才发现可以把初始化里面的约分提出来,用一个单独的函数,这样还能再方便一点,不然要写两次约分。最后就是在除法中检验分母不能为0的时候,这边已经判断了分母不能为0的时候,我就让他输出这个提示,但是它会多输出来一个0,即使我用 return NuLL了,它依然多输出来一个0。不知道为什么,所以我只能把提示变成“分母不能为”这样结果就正常了。
浙公网安备 33010602011771号