实验2
实验二:
实验1
源代码:
T.h
1 #pragma once 2 3 #include <string> 4 5 // 类T: 声明 6 class T { 7 // 对象属性、方法 8 public: 9 T(int x = 0, int y = 0); // 普通构造函数 10 T(const T& t); // 复制构造函数 11 T(T&& t); // 移动构造函数 12 ~T(); // 析构函数 13 14 void adjust(int ratio); // 按系数成倍调整数据 15 void display() const; // 以(m1, m2)形式显示T类对象信息 16 17 private: 18 int m1, m2; 19 20 // 类属性、方法 21 public: 22 static int get_cnt(); // 显示当前T类对象总数 23 24 public: 25 static const std::string doc; // 类T的描述信息 26 static const int max_cnt; // 类T对象上限 27 28 private: 29 static int cnt; // 当前T类对象数目 30 31 // 类T友元函数声明 32 friend void func(); 33 }; 34 35 // 普通函数声明 36 void func();
T.cpp
1 #include "T.h" 2 #include <iostream> 3 #include <string> 4 5 // 类T实现 6 7 // 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 // 类方法 13 int T::get_cnt() { 14 return cnt; 15 } 16 17 // 对象方法 18 T::T(int x, int y) : m1{ x }, m2{ y } { 19 ++cnt; 20 std::cout << "T constructor called.\n"; 21 } 22 23 T::T(const T& t) : m1{ t.m1 }, m2{ t.m2 } { 24 ++cnt; 25 std::cout << "T copy constructor called.\n"; 26 } 27 28 T::T(T&& t) : m1{ t.m1 }, m2{ t.m2 } { 29 ++cnt; 30 std::cout << "T move constructor called.\n"; 31 } 32 33 T::~T() { 34 --cnt; 35 std::cout << "T destructor called.\n"; 36 } 37 38 void T::adjust(int ratio) { 39 m1 *= ratio; 40 m2 *= ratio; 41 } 42 43 void T::display() const { 44 std::cout << "(" << m1 << ", " << m2 << ")"; 45 } 46 47 // 普通函数实现 48 void func() { 49 T t5(42); 50 t5.m2 = 2049; 51 std::cout << "t5 = "; t5.display(); std::cout << '\n'; 52 }
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.NO,报错截图如下:

原因:在 C++ 中,在类内声明的友元函数仅表示该函数可访问类的私有成员,还需要在类外进行函数声明,否则编译器在调用该函数时无法识别其存在。若删除 void func(); 这条函数声明语句,当程序中调用 func() 时,编译器会因func函数未声明而报错无法运行。
2.普通构造函数 T(int x = 0, int y = 0);
(1)功能:用于创建T类对象时初始化成员变量m1和m2.
(2)调用时机:当使用T t1、T t2(3,4)带参数或者不带参数等方式创建T类对象时调用。
复制构造函数 T(const T& t);
(1)功能:通过已存在的T类对象,创建一个完全相同的新T类对象,达到复制的作用。
(2)调用时机:用一个已存在的T对象初始化另一个新T对象,在task1中应用为T t3(t2)操作。
移动构造函数 T(T&& t);
(1)功能:利用右值引用,将一个临时T对象的右值移动到新对象中。
(2)调用时机:当用一个右值初始化新T对象时,在task1中为T t4(std::move(t2))操作。
析构函数 ~T();
(1)功能:在T类对象生命周期结束时,将动态分配的内存释放掉。
(2)调用时机:当T类对象离开其作用域,通过delete操作释放动态分配的T对象时被调用。
3.编译失败,报错截图如下:

原因:T::get_cnt()函数被重复定义了,其在T.h中原先已被定义过,因此程序报错。
实验二
源代码:
Complex.h
1 #pragma once 2 #include <string> 3 4 class Complex { 5 public: 6 static const std::string doc; 7 8 Complex(double real = 0.0, double imag = 0.0); 9 Complex(const Complex& other); 10 11 double get_real() const; 12 double get_imag() const; 13 void add(const Complex& other); 14 15 friend void output(const Complex& c); 16 friend double abs(const Complex& c); 17 friend Complex add(const Complex& c1, const Complex& c2); 18 friend bool is_equal(const Complex& c1, const Complex& c2); 19 friend bool is_not_equal(const Complex& c1, const Complex& c2); 20 21 private: 22 double real; 23 double imag; 24 };
Complex.cpp
1 #include "Complex.h" 2 #include <cmath> 3 #include <iostream> 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 Complex::Complex(const Complex& other) : real(other.real), imag(other.imag) {} 11 12 // 对象方法实现 13 double Complex::get_real() const { return real; } 14 double Complex::get_imag() const { return imag; } 15 void Complex::add(const Complex& other) { 16 real += other.real; 17 imag += other.imag; 18 } 19 20 // 友元函数实现 21 void output(const Complex& c) { 22 std::cout << c.real; 23 if (c.imag >= 0) std::cout << " + " << c.imag << "i"; 24 else std::cout << " - " << -c.imag << "i"; 25 } 26 27 double abs(const Complex& c) { 28 return std::sqrt(c.real * c.real + c.imag * c.imag); 29 } 30 31 Complex add(const Complex& c1, const Complex& c2) { 32 return Complex(c1.real + c2.real, c1.imag + c2.imag); 33 } 34 35 bool is_equal(const Complex& c1, const Complex& c2) { 36 return (c1.real == c2.real) && (c1.imag == c2.imag); 37 } 38 39 bool is_not_equal(const Complex& c1, const Complex& c2) { 40 return !is_equal(c1, c2); 41 }
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 << "\n*******测试2:标准库模板类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 22 cout << "类成员测试:" << endl; 23 cout << Complex::doc << endl << endl; 24 25 cout << "Complex对象测试:" << endl; 26 Complex c1; 27 Complex c2(3, -4); 28 Complex c3(c2); 29 Complex c4 = c2; 30 const Complex c5(3.5); 31 32 cout << "c1 = "; output(c1); cout << endl; 33 cout << "c2 = "; output(c2); cout << endl; 34 cout << "c3 = "; output(c3); cout << endl; 35 cout << "c4 = "; output(c4); cout << endl; 36 cout << "c5.real = " << c5.get_real() 37 << ", c5.imag = " << c5.get_imag() << endl << endl; 38 39 cout << "复数运算测试:" << endl; 40 cout << "abs(c2) = " << abs(c2) << endl; 41 c1.add(c2); 42 cout << "c1 += c2, c1 = "; output(c1); cout << endl; 43 cout << boolalpha; 44 cout << "c1 == c2 : " << is_equal(c1, c2) << endl; 45 cout << "c1 != c2 : " << is_not_equal(c1, c2) << endl; 46 c4 = add(c2, c3); 47 cout << "c4 = c2 + c3, c4 = "; output(c4); cout << endl; 48 } 49 50 void test_std_complex() { 51 using std::cout; 52 using std::endl; 53 using std::boolalpha; 54 55 cout << "std::complex<double>对象测试:" << endl; 56 std::complex<double> c1; 57 std::complex<double> c2(3, -4); 58 std::complex<double> c3(c2); 59 std::complex<double> c4 = c2; 60 const std::complex<double> c5(3.5); 61 62 cout << "c1 = (" << c1.real() << "," << c1.imag() << ")" << endl; 63 cout << "c2 = (" << c2.real() << "," << c2.imag() << ")" << endl; 64 cout << "c3 = (" << c3.real() << "," << c3.imag() << ")" << endl; 65 cout << "c4 = (" << c4.real() << "," << c4.imag() << ")" << endl; 66 cout << "c5.real = " << c5.real() 67 << ", c5.imag = " << c5.imag() << endl << endl; 68 69 cout << "复数运算测试:" << endl; 70 cout << "abs(c2) = " << std::abs(c2) << endl; 71 c1 += c2; 72 cout << "c1 += c2, c1 = (" << c1.real() << "," << c1.imag() << ")" << endl; 73 cout << boolalpha; 74 cout << "c1 == c2 : " << (c1 == c2) << endl; 75 cout << "c1 != c2 : " << (c1 != c2) << endl; 76 c4 = c2 + c3; 77 cout << "c4 = c2 + c3, c4 = (" << c4.real() << "," << c4.imag() << ")" << endl; 78 }
运行结果:

问题回答:
1.显然标准库模板类 complex 更加简洁,其支持原生运算符(+=、+、==、!=)和流输出操作,形式更贴近自然数学语言的表达,增加了代码的可读性。
函数和运算内在关联:自定义类的函数(如 add、is_equal)与标准库的运算符(+=、==)功能等价,只不过是对数学运算的的封装形式的不同。
2.1 是:output 需要访问复数的实部和虚部以格式化输出;abs 需要读取这两个私有成员用来计算模长;add 需要操作私有成员实现加法,因此必须设为友元。
2.2 否:标准库 std::complex 的 abs 是普通函数,通过类的公有接口访问数据,未使用友元。
2.3 当外部函数需要直接访问类的私有成员,且该函数与类的逻辑强关联时,可以使用友元。
3.将复制构造函数设为私有,即 private: Complex(const Complex &other)操作,这样类似 Complex c4 = c2的等于形式的复制初始化会因无法访问私有构造函数而编译报错。
实验三
源代码:
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 10 ControlType parse(const std::string& control_str); // 实现std::string --> ControlType转换 11 void execute(ControlType cmd) const; // 执行控制操作(以打印输出模拟) 12 13 static int get_cnt(); 14 15 private: 16 static int total_cnt; 17 };
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 // 待补足 10 // 1. 将输入字符串转为小写,实现大小写不敏感 11 // 2. 匹配"play"/"pause"/"next"/"prev"/"stop"并返回对应枚举 12 // 3. 未匹配的字符串返回ControlType::Unknown 13 // 4. 每次成功调用parse时递增total_cnt 14 ControlType PlayerControl::parse(const std::string& control_str) { 15 // 1. 将输入字符串转为小写(忽略大小写) 16 std::string lower_str = control_str; 17 std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(), 18 [](unsigned char c) { return std::tolower(c); }); 19 20 // 2. 匹配命令并返回对应枚举 21 ControlType cmd = ControlType::Unknown; 22 if (lower_str == "play") cmd = ControlType::Play; 23 else if (lower_str == "pause") cmd = ControlType::Pause; 24 else if (lower_str == "next") cmd = ControlType::Next; 25 else if (lower_str == "prev") cmd = ControlType::Prev; 26 else if (lower_str == "stop") cmd = ControlType::Stop; 27 28 // 3. 成功匹配则递增操作次数 29 if (cmd != ControlType::Unknown) { 30 PlayerControl::total_cnt++; 31 } 32 return cmd; 33 34 } 35 36 void PlayerControl::execute(ControlType cmd) const { 37 switch (cmd) { 38 case ControlType::Play: std::cout << "[play] Playing music...\n"; break; 39 case ControlType::Pause: std::cout << "[Pause] Music paused\n"; break; 40 case ControlType::Next: std::cout << "[Next] Skipping to next track\n"; break; 41 case ControlType::Prev: std::cout << "[Prev] Back to previous track\n"; break; 42 case ControlType::Stop: std::cout << "[Stop] Music stopped\n"; break; 43 default: std::cout << "[Error] unknown control\n"; break; 44 } 45 } 46 47 int PlayerControl::get_cnt() { 48 return total_cnt; 49 }
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 的现代播放控制输出,可以直接修改 PlayerControl.cpp 中 execute 函数的输出内容部分?如 case ControlType::Play: std::cout << "🎵 Playing music...\n"; break,以此类推。
实验四
源代码:
Fraction.h
1 #ifndef FRACTION_H 2 #define FRACTION_H 3 4 #include <string> 5 6 class Fraction { 7 private: 8 int up; // 分子 9 int down; // 分母 10 11 int gcd(int a, int b); // 求最大公约数 12 void reduce(); // 约分 13 14 public: 15 static const std::string doc; // 类说明常量 16 17 // 构造函数 18 Fraction(int up = 0, int down = 1); 19 Fraction(const Fraction& other); // 拷贝构造 20 21 // 接口 22 int get_up() const; 23 int get_down() const; 24 Fraction negative() const; // 求负运算 25 }; 26 27 // 工具函数声明 28 void output(const Fraction& f); 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 #endif // FRACTION_H
Fraction.cpp
1 #include "Fraction.h" 2 #include <iostream> 3 #include <stdexcept> 4 5 const std::string Fraction::doc = "Fraction类 v0.01版.\n目前仅支持分数对象的构造、输出、加/减/乘/除运算."; 6 7 // 构造函数实现时移除默认参数(默认参数仅在头文件声明中指定) 8 Fraction::Fraction(int up, int down) { 9 if (down == 0) { 10 throw std::invalid_argument("分母不能为0"); 11 } 12 this->up = up; 13 this->down = down; 14 reduce(); 15 } 16 17 Fraction::Fraction(const Fraction& other) : up(other.up), down(other.down) {} 18 19 int Fraction::get_up() const { return up; } 20 int Fraction::get_down() const { return down; } 21 22 Fraction Fraction::negative() const { 23 return Fraction(-up, down); 24 } 25 26 int Fraction::gcd(int a, int b) { 27 a = std::abs(a); 28 b = std::abs(b); 29 while (b != 0) { 30 int temp = b; 31 b = a % b; 32 a = temp; 33 } 34 return a; 35 } 36 37 void Fraction::reduce() { 38 if (down < 0) { 39 up = -up; 40 down = -down; 41 } 42 int g = gcd(up, down); 43 if (g != 0) { 44 up /= g; 45 down /= g; 46 } 47 } 48 49 void output(const Fraction& f) { 50 if (f.get_down() == 1) { 51 std::cout << f.get_up(); 52 } 53 else { 54 std::cout << f.get_up() << "/" << f.get_down(); 55 } 56 } 57 58 Fraction add(const Fraction& f1, const Fraction& f2) { 59 int up = f1.get_up() * f2.get_down() + f2.get_up() * f1.get_down(); 60 int down = f1.get_down() * f2.get_down(); 61 return Fraction(up, down); 62 } 63 64 Fraction sub(const Fraction& f1, const Fraction& f2) { 65 int up = f1.get_up() * f2.get_down() - f2.get_up() * f1.get_down(); 66 int down = f1.get_down() * f2.get_down(); 67 return Fraction(up, down); 68 } 69 70 Fraction mul(const Fraction& f1, const Fraction& f2) { 71 int up = f1.get_up() * f2.get_up(); 72 int down = f1.get_down() * f2.get_down(); 73 return Fraction(up, down); 74 } 75 76 Fraction div(const Fraction& f1, const Fraction& f2) { 77 if (f2.get_up() == 0) { 78 std::cerr << "分母不能为0" << std::endl; 79 return Fraction(0, 1); 80 } 81 int up = f1.get_up() * f2.get_down(); 82 int down = f1.get_down() * f2.get_up(); 83 return Fraction(up, down); 84 }
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 11 std::cout << "\n测试2: 分母为0测试: \n"; 12 test2(); 13 } 14 15 void test1() { 16 using std::cout; 17 using std::endl; 18 19 cout << "Fraction类测试: " << endl; 20 cout << Fraction::doc << endl << endl; 21 22 Fraction f1(5); 23 Fraction f2(3, -4), f3(-18, 12); 24 Fraction f4(f3); 25 cout << "f1 = "; output(f1); cout << endl; 26 cout << "f2 = "; output(f2); cout << endl; 27 cout << "f3 = "; output(f3); cout << endl; 28 cout << "f4 = "; output(f4); cout << endl; 29 30 const Fraction f5(f4.negative()); 31 cout << "f5 = "; 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 = "; output(add(f1, f2)); cout << endl; 36 cout << "f1 - f2 = "; output(sub(f1, f2)); cout << endl; 37 cout << "f1 * f2 = "; output(mul(f1, f2)); cout << endl; 38 cout << "f1 / f2 = "; output(div(f1, f2)); cout << endl; 39 cout << "f4 + f5 = "; 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 48 cout << "f6 = "; output(f6); cout << endl; 49 cout << "f7 = "; output(f7); cout << endl; 50 cout << "f6 / f7 = "; output(div(f6, f7)); cout << endl; 51 }
运行截图:

问题回答:
使用自由函数:(1)自由函数无需依赖类的友元权限,可以独立实现分数的输出和运算逻辑,代码解耦性强、可读性高。
(2)友元虽然可以直接访问私有成员,但会破坏类的封装性,自由函数在该场景下兼顾了功能独立性与实现简洁性。
实验总结
(1)慢慢从c语言过渡到c++的语法中去,明确了类与对象的封装特性,掌握了 C++ 类的定义、实现与测试方法,能熟练创建对象并基于面向对象编程。
(2)理解了构造函数在对象初始化时的调用机制,学会了多文件组织代码的操作。
(3)学会根据场景合理运用static、const、友元等 C++ 特性,在数据共享与保护间取得平衡,提升了面向对象编程思维的设计能力。
 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号