实验二
实验任务1
T.h源码
#pragma once #include <string> // 类T: 声明 class T { // 对象属性、方法 public: T(int x = 0, int y = 0); // 普通构造函数 T(const T &t); // 复制构造函数 T(T &&t); // 移动构造函数 ~T(); // 析构函数 void adjust(int ratio); // 按系数成倍调整数据 void display() const; // 以(m1, m2)形式显示T类对象信息 private: int m1, m2; // 类属性、方法 public: static int get_cnt(); // 显示当前T类对象总数 public: static const std::string doc; // 类T的描述信息 static const int max_cnt; // 类T对象上限 private: static int cnt; // 当前T类对象数目 // 类T友元函数声明 friend void func(); }; // 普通函数声明 void func();
T.cpp源码
#include "T.h" #include <iostream> #include <string> // 类T实现 // static成员数据类外初始化 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 << "test 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 objects'max count: " << T::max_cnt << endl; cout << "T objects'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 objects'current count: " << T::get_cnt() << endl; }

问题1:
YES
问题2:
T(int x=0, int y=0) 普通构造函数 创建对象时调用 T(const T& t) 拷贝构造函数 用已有对象初始化新对象时调用 T(T&& t) 移动构造函数 用右值(临时对象)初始化新对象时调用 ~T() 析构函数 对象生命周期结束时调用
问题3:
不能正确编译

原因: T.cpp 中 line13-15 是 静态成员的定义(非声明),必须放在 实现文件(.cpp) 中。若剪切到 T.h 末尾,当 T.h 被多个源文件包含时,会导致 多重定义(ODR违规),链接时报错。
实验任务2:
Complex.h代码:
#pragma once 
#include  class Complex { public:  static const std::string doc;  
Complex(double r = 0.0, double i = 0.0);  
Complex(const Complex& rhs); 
Complex(Complex&& rhs) noexcept;  
double get_real() const { return real; }  
double get_imag() const { return imag; }  
void add(const Complex& rhs); 
private:  double real, imag; 
public:  friend void output(const Complex& c);  
friend double abs(const Complex& c);  friend Complex add(const Complex& a, const Complex& b);  
friend bool is_equal(const Complex& a, const Complex& b);  
friend bool is_not_equal(const Complex& a, const Complex& b); };
Complex.cpp代码:
#include "Complex.h" 
#include  #include  const std::string Complex::doc = "a simplified complex class"; 
Complex::Complex(double r, double i) : real(r), imag(i) {} 
Complex::Complex(const Complex& rhs) : real(rhs.real), imag(rhs.imag) {} 
Complex::Complex(Complex&& rhs) noexcept : real(rhs.real), imag(rhs.imag) {} 
void Complex::add(const Complex& rhs) { real += rhs.real; imag += rhs.imag; } 
void output(const Complex& c) {  std::cout << c.real << (c.imag >= 0 ? '+' : '-') << std::fabs(c.imag) << 'i'; } 
double abs(const Complex& c) { return std::sqrt(c.real * c.real + c.imag * c.imag); } 
Complex add(const Complex& a, const Complex& b) { return Complex(a.real + b.real, a.imag + b.imag); } 
bool is_equal(const Complex& a, const Complex& b) { return a.real == b.real && a.imag == b.imag; } 
bool is_not_equal(const Complex& a, const Complex& b) { return !(a == b); }
task2代码:
#include "Complex.h"
#include  void test_Complex() {  using std::cout; using std::endl; using std::boolalpha;  
cout << "类成员测试:\n" << Complex::doc << "\n\nComplex对象测试:\n";  
Complex c1, c2(3, -4), c3(c2), c4 = c2; 
const Complex c5(3.5);  cout << "c1="; output(c1); 
cout << "\nc2="; output(c2); cout << "\nc3="; output(c3);  cout << "\nc4="; output(c4); 
cout << "\nc5.real=" << c5.get_real() << ", c5.imag=" << c5.get_imag() << "\n\n复数运算测试:\n";  cout << "abs(c2)=" << abs(c2) << '\n'; 
c1.add(c2); cout << "c1+=c2, c1="; output(c1); cout << '\n';  
cout << boolalpha << "c1==c2:" << is_equal(c1, c2) << "\nc1!=c2:" << is_not_equal(c1, c2) << '\n';  c4 = add(c2, c3); 
cout << "c4=c2+c3, c4="; output(c4); cout << '\n'; } void test_std_complex() {  using std::cout; using std::endl; using std::boolalpha;  
cout << "std::complex对象测试:\n";  
std::complex c1, c2(3, -4), c3(c2), c4 = c2; const std::complex c5(3.5);  
cout << "c1=" << c1 << "\nc2=" << c2 << "\nc3=" << c3 << "\nc4=" << c4  << "\nc5.real=" << c5.real() << ", c5.imag=" << c5.imag() << "\n\n复数运算测试:\n";  
cout << "abs(c2)=" << std::abs(c2) << '\n'; c1 += c2; 
cout << "c1+=c2, c1=" << c1 << '\n';  
cout << boolalpha << "c1==c2:" << (c1 == c2) << "\nc1!=c2:" << (c1 != c2) << '\n';  
c4 = c2 + c3; cout << "c4=c2+c3, c4=" << c4 << '\n'; } 
int main() {  std::cout << "测试1:自定义类Complex \n"; test_Complex();  
std::cout << "\n测试2:标准库模板类complex \n"; test_std_complex();  
return 0; 
}


问题1:
标准库更简洁,运算符的重载使得用法简洁,而自定义类需要手工调用
两组函数语义完全对应,只是把运算符展开成命名函数
问题2:
是
当前实现把 real、imag 设为 private,而 output 需要直接读写两成员格式化输出;abs 需要实部虚部计算模;add 需要直接拿两对象的实部虚部构造新对象。若不舍弃封装,则现阶段必须 friend。
仅当非成员函数必须访问私有或公有数据,且通过公有接口会引入明显的性能损失或语义存在歧义时考虑使用
问题3:
在 Complex 声明 public 区段显式删除拷贝构造即可
实验任务3
PlayerControl.h代码:
#pragma once
#include enum class ControlType { Play, Pause, Next, Prev, Stop, Unknown };
class PlayerControl { public: PlayerControl();
ControlType parse(const std::string& controlStr);
void execute(ControlType cmd) const;
static int get_cnt() { return total_cnt; }
private: static int total_cnt; };
PlayerControl.cpp代码:
#include "PlayerControl.h"
#include int PlayerControl::total_cnt = 0;
PlayerControl::PlayerControl() = default;
ControlType PlayerControl::parse(const std::string& controlStr)
{ std::string s = controlStr;
std::transform(s.begin(), s.end(), s.begin(), [] { return std::tolower(c); });
ControlType ret = ControlType::Unknown;
if (s == "play") ret = ControlType::Play;
else if (s == "pause") ret = ControlType::Pause;
else if (s == "next") ret = ControlType::Next;
else if (s == "prev") ret = ControlType::Prev;
else if (s == "stop") ret = ControlType::Stop;
if (ret != ControlType::Unknown) ++total_cnt;
return ret;
}
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; } }
task3.cpp代码:
#include "PlayerControl.h"
#include void test() { PlayerControl controller; std::string controlStr; std::cout << "Enter Control: (play/pause/next/prev/stop/quit):\n";
while (std::cin >> controlStr) {
if (controlStr == "quit")
break;
ControlType cmd = controller.parse(controlStr);
controller.execute(cmd);
std::cout << "Current Player control: " << PlayerControl::get_cnt() << "\n\n"; } }
int main() { test();
return 0; }

实验任务4:
Fraction.h代码:
Fraction.h #pragma once #include class Fraction { public: static const std::string doc; Fraction(int u = 0, int d = 1);
Fraction(const Fraction& f);
int get_up() const;
int get_down() const;
Fraction negative() const;
private: int up, down; void simplify();
static int gcd(int a, int b);
friend void output(const Fraction& f);
friend Fraction add(const Fraction& a, const Fraction& b);
friend Fraction sub(const Fraction& a, const Fraction& b);
friend Fraction mul(const Fraction& a, const Fraction& b);
friend Fraction div(const Fraction& a, const Fraction& b);
};
Fraction.cpp代码:
Fraction.cpp #include "Fraction.h"
#include const std::string Fraction::doc = "Fraction类v0.01版";
int Fraction::gcd(int a, int b) { return b == 0 ? std::abs(a) : gcd(b, a % b); } void Fraction::simplify() { int g = gcd(up, down);
up /= g; down /= g;
if (down < 0) { up = -up; down = -down; } }
Fraction::Fraction(int u, int d) : up(u), down(d) { if (down == 0) { down = 1; } simplify(); }
Fraction::Fraction(const Fraction& f) : up(f.up), down(f.down) {} int Fraction::get_up() const { return up; }
int Fraction::get_down() const { return down; }
Fraction Fraction::negative() const { return Fraction(-up, down); }
void output(const Fraction& f) { if (f.down == 1) std::cout << f.up;
else std::cout << f.up << '/' << f.down; }
Fraction add(const Fraction& a, const Fraction& b)
{ return Fraction(a.up * b.down + b.up * a.down, a.down * b.down); }
Fraction sub(const Fraction& a, const Fraction& b) { return Fraction(a.up * b.down - b.up * a.down, a.down * b.down); }
Fraction mul(const Fraction& a, const Fraction& b) { return Fraction(a.up * b.up, a.down * b.down); }
Fraction div(const Fraction& a, const Fraction& b) { if (b.up == 0) { std::cout << "分母不能为0"; return Fraction(0, 1); }
return Fraction(a.up * b.down, a.down * b.up);
}
task4.cpp代码:
#include "Fraction.h"
#include void test1() { using namespace std; cout << Fraction::doc << endl;
Fraction f1(5), f2(3, -4), f3(-18, 12), f4(f3); const Fraction f5(f4.negative());
cout << "f1="; output(f1); cout << "\nf2="; output(f2);
cout << "\nf3="; output(f3); cout << "\nf4="; output(f4);
cout << "\nf5="; output(f5); cout << "\nf5.get_up()=" << f5.get_up() << ", f5.get_down()=" << f5.get_down() << "\n";
cout << "f1+f2="; output(add(f1, f2)); cout << "\nf1-f2=";
output(sub(f1, f2));
cout << "\nf1*f2="; output(mul(f1, f2)); cout << "\nf1/f2=";
output(div(f1, f2)); cout << "\nf4+f5="; output(add(f4, f5)); cout << endl; }
void test2() { using namespace std; Fraction f6(42, 55), f7(0, 3); cout << "f6=";
output(f6); cout << "\nf7=";
output(f7); cout << "\nf6/f7=";
output(div(f6, f7)); cout << endl; }
int main() { std::cout << "测试1:Fraction类基础功能测试\n";
test1(); std::cout << "\n测试2:分母为0测试:\n";
test2();
return 0;
}

问题:
采用友元方案。理由:需直接访问私有分子分母并返回新Fraction,友元兼顾封装与效率;若用static成员函数则调用形式冗长,自由函数+getter会多次构造降低性能,命名空间方案同样需暴露数据,故选友元。
 
                    
                 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号