实验2 现代C++编程初体验

实验任务1

源码

 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 }

T.h

 1 #pragma once
 2 #include <string>
 3 
 4 class T {
 5 public:
 6     T(int x = 0, int y = 0);    // 普通构造函数
 7     T(const T &t);              // 拷贝构造函数
 8     T(T &&t);                   // 移动构造函数
 9     ~T();                       // 析构函数
10 
11     void adjust(int ratio);     // 按比例调整
12     void display() const;       // 输出对象信息
13 
14 private:
15     int m1, m2;
16 
17 public:
18     static int get_cnt();       // 获取对象总数
19     static const std::string doc;
20     static const int max_cnt;
21 
22 private:
23     static int cnt;
24 
25     friend void func();         // 友元函数
26 };
27 
28 void func();

task.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 }

 

 

运行测试结果截图:

image

 

问题1:

T.h中,在类T内部,已声明func 是T的友元函数。在类外部,去掉line36,重新编译,程序能否正常运行?如果能,回答YES;如果不能,以截图形式提供编译报错信息,说明原因。

答:去掉友元声明后,编译错误。func()无法访问类的私有成员m2.

image

 

问题2:

T.h中,line9-12给出了各种构造函数、析构函数。总结它们各自的功能、调用时机。

答:

构造函数与析构函数的作用与调用时机:

普通构造:创建对象时;

拷贝构造:以对象初始化对象;

移动构造:右值或临时对象转移;

析构:生命周期结束时。

问题3:

T.cpp中,line13-15,剪切到T.h的末尾,重新编译,程序能否正确编译。
如不能,以截图形式给出报错信息,分析原因。

答:静态成员初始化语句应位于.cpp文件中第8–10行(非13–15)。

若放入.h文件会造成重复定义错误。

image

 

实验任务2

源码:

 Complex.h

 1 #pragma once
 2 #include <string>
 3 #include <cmath>
 4 
 5 class Complex {
 6 public:
 7     Complex(double r = 0.0, double i = 0.0);
 8     Complex(const Complex &c);
 9 
10     double get_real() const;
11     double get_imag() const;
12     void add(const Complex &c);
13 
14     static const std::string doc;
15 
16 private:
17     double real, imag;
18     friend void output(const Complex &c);
19     friend double abs(const Complex &c);
20     friend Complex add(const Complex &a, const Complex &b);
21     friend bool is_equal(const Complex &a, const Complex &b);
22     friend bool is_not_equal(const Complex &a, const Complex &b);
23 };

Complex.cpp

 1 #include "Complex.h"
 2 #include <iostream>
 3 
 4 const std::string Complex::doc = "a simplified complex class";
 5 
 6 Complex::Complex(double r, double i): real{r}, imag{i} {}
 7 Complex::Complex(const Complex &c): real{c.real}, imag{c.imag} {}
 8 
 9 double Complex::get_real() const { return real; }
10 double Complex::get_imag() const { return imag; }
11 
12 void Complex::add(const Complex &c) {
13     real += c.real;
14     imag += c.imag;
15 }
16 
17 void output(const Complex &c) {
18     std::cout << c.real << (c.imag >= 0 ? " + " : " - ")
19               << std::abs(c.imag) << "i";
20 }
21 
22 double abs(const Complex &c) {
23     return std::sqrt(c.real * c.real + c.imag * c.imag);
24 }
25 
26 Complex add(const Complex &a, const Complex &b) {
27     return Complex(a.real + b.real, a.imag + b.imag);
28 }
29 
30 bool is_equal(const Complex &a, const Complex &b) {
31     return a.real == b.real && a.imag == b.imag;
32 }
33 
34 bool is_not_equal(const Complex &a, const Complex &b) {
35     return !(is_equal(a, b));
36 }

 

运行测试结果截图:

 

image

 

问题1:
比较自定义类complex 和标准库模板类complex的用法,
complex 的用法,在使用形式上,哪一种更简洁?函数和运算内
在有关联吗?

答:标准库的std::complex更简洁,因为它使用了运算符重载,而自定义类通过函数实现。

 

 

问题2:
2-1:自定义complex中 的用法,output/abs/add/ 等均设为友元,它们真的需要访问私有数据吗?

答:output/add等函数确实需要访问私有数据,因此设置为friend。

2-2:标准库std::complex 是否把 abs 设为友元?

答:标准库abs不是友元,而是独立函数。
2-3:什么时候才考虑使用 friend?总结你的思考。

答:只有当外部函数确实需要访问私有数据时,才使用friend,避免破坏封装性。

问题3:
如果构造对象时禁用=形式,即遇到complex c4 = c2;编译报错,类complex的设计应如何调整?

答:将 Complex 类的拷贝构造函数声明为 explicit。

实验任务3

源码:

 

 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 }

 

运行测试结果截图:

 

image

 

实验任务4

源码:

 

 1 #pragma once
 2 #include <string>
 3 
 4 class Fraction {
 5 public:
 6     Fraction(int up = 0, int down = 1);
 7     Fraction(const Fraction& f);
 8 
 9     int get_up() const;
10     int get_down() const;
11     Fraction negative() const;
12 
13     static const std::string doc;
14 
15 private:
16     int up, down;
17     void simplify();
18 
19     friend void output(const Fraction& f);
20     friend Fraction add(const Fraction& a, const Fraction& b);
21     friend Fraction sub(const Fraction& a, const Fraction& b);
22     friend Fraction mul(const Fraction& a, const Fraction& b);
23     friend Fraction div(const Fraction& a, const Fraction& b);
24 };
 1 #include "Fraction.h"
 2 #include <iostream>
 3 #include <numeric>
 4 #include <stdexcept>
 5 
 6 const std::string Fraction::doc = "Fraction类 v0.01版";
 7 
 8 Fraction::Fraction(int u, int d): up{u}, down{d} {
 9     if (down == 0) throw std::invalid_argument("Denominator cannot be zero");
10     simplify();
11 }
12 
13 Fraction::Fraction(const Fraction& f): up{f.up}, down{f.down} {}
14 
15 void Fraction::simplify() {
16     if (down < 0) { down = -down; up = -up; }
17     int g = std::gcd(up, down);
18     if (g != 0) { up /= g; down /= g; }
19 }
20 
21 int Fraction::get_up() const { return up; }
22 int Fraction::get_down() const { return down; }
23 
24 Fraction Fraction::negative() const {
25     return Fraction(-up, down);
26 }
27 
28 void output(const Fraction& f) {
29     std::cout << f.up << "/" << f.down;
30 }
31 
32 Fraction add(const Fraction& a, const Fraction& b) {
33     return Fraction(a.up * b.down + b.up * a.down, a.down * b.down);
34 }
35 
36 Fraction sub(const Fraction& a, const Fraction& b) {
37     return Fraction(a.up * b.down - b.up * a.down, a.down * b.down);
38 }
39 
40 Fraction mul(const Fraction& a, const Fraction& b) {
41     return Fraction(a.up * b.up, a.down * b.down);
42 }
43 
44 Fraction div(const Fraction& a, const Fraction& b) {
45     if (b.up == 0) throw std::invalid_argument("Division by zero");
46     return Fraction(a.up * b.down, a.down * b.up);
47 }

 

运行测试结果截图:

 

image

 

问题:分数的输出和计算,output/add/sub/mul/div ,你选择的是哪一种设计方案?(友元/自由函数/命名空间+自由函数/类+static)
你的决策理由?如友元方案的优缺点、静态成员函数方案的适用场景、命名空间方案的考虑因素等。

答:采用友元函数方案实现 output/add/sub/mul/div。

理由:它们需要访问分子分母的私有成员,友元方式简洁直观。

若仅作读操作,亦可使用命名空间自由函数,但友元更贴合此场景。

 

posted @ 2025-10-26 21:05  溯溪而上  阅读(0)  评论(0)    收藏  举报