实验二

实验任务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;
}

屏幕截图 2025-10-29 000505

问题1:

YES

问题2:

  T(int x=0, int y=0)  普通构造函数  创建对象时调用    T(const T& t)   拷贝构造函数  用已有对象初始化新对象时调用   T(T&& t)  移动构造函数  用右值(临时对象)初始化新对象时调用   ~T()   析构函数  对象生命周期结束时调用

问题3:

不能正确编译

image

 

原因: 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;
}

image

image

 问题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; }

image

实验任务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;
}

image

问题:

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

 

posted @ 2025-10-29 01:16  15468483  阅读(2)  评论(0)    收藏  举报