实验2 类和对象_基础编程1

实验任务一:

 1 // 类T: 实现
 2 // 普通函数实现
 3 
 4 #include "t.h"
 5 #include <iostream>
 6 #include <string>
 7 
 8 using std::cout;
 9 using std::endl;
10 using std::string;
11 
12 // static成员数据类外初始化
13 const std::string T::doc{"a simple class sample"};
14 const int T::max_cnt = 999;
15 int T::cnt = 0;
16 
17 
18 // 对象方法
19 T::T(int x, int y): m1{x}, m2{y} { 
20     ++cnt; 
21     cout << "T constructor called.\n";
22 } 
23 
24 T::T(const T &t): m1{t.m1}, m2{t.m2} {
25     ++cnt;
26     cout << "T copy constructor called.\n";
27 }
28 
29 T::T(T &&t): m1{t.m1}, m2{t.m2} {
30     ++cnt;
31     cout << "T move constructor called.\n";
32 }    
33 
34 T::~T() {
35     --cnt;
36     cout << "T destructor called.\n";
37 }           
38 
39 void T::adjust(int ratio) {
40     m1 *= ratio;
41     m2 *= ratio;
42 }    
43 
44 void T::display() const {
45     cout << "(" << m1 << ", " << m2 << ")" ;
46 }     
47 
48 // 类方法
49 int T::get_cnt() {
50    return cnt;
51 }
52 
53 // 友元
54 void func() {
55     T t5(42);
56     t5.m2 = 2049;
57     cout << "t5 = "; t5.display(); cout << endl;
58 }
t.cpp
 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.h
 1 #include "t.h"
 2 #include <iostream>
 3 
 4 using std::cout;
 5 using std::endl;
 6 
 7 void test();
 8 
 9 int main() {
10     test();
11     cout << "\nmain: \n";
12     cout << "T objects'current count: " << T::get_cnt() << endl;
13 }
14 
15 void test() {
16     cout << "test class T: \n";
17     cout << "T info: " << T::doc << endl;
18     cout << "T objects'max count: " << T::max_cnt << endl;
19     cout << "T objects'current count: " << T::get_cnt() << endl << endl;
20 
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 << "t3 = "; t4.display(); cout << endl;
34 
35     cout << "T objects'current count: " << T::get_cnt() << endl;
36 
37     func();
38 }
task1.cpp

问题一:编译会失败

 原因:如果去掉友元函数声明,编译会失败,因为func需要访问类的私有成员。

问题二

功能:

1. 普通构造函数 T(int x = 0, int y = 0)**:这是该类的默认构造函数,用于初始化成员变量 m1 和 m2。当创建类 T 的实例时,如果没有提供具体的参数,系统将自动调用这个构造函数进行初始化。

2. 复制构造函数 T(const T &t)**:这个构造函数用于生成一个新对象,该对象是从另一个同类型对象复制而来的。它通过复制源对象的成员变量来设置新对象的状态。

3. 移动构造函数 T(T &&t)**:移动构造函数允许在不进行数据复制的情况下,将一个对象的资源转移到另一个对象。它接收一个右值引用作为参数。

4. 析构函数 ~T()**:析构函数用于执行清理工作,以释放对象所占用的资源。

调用时机:

普通构造函数在对象创建时被调用。

复制构造函数在通过已有对象创建新对象时被激活。
 移动构造函数在将一个对象移动到另一个对象时被调用。
析构函数在对象生命周期结束时被调用,例如当对象超出作用域或被显式删除时。

问题三:

不能正常运行。

因为静态成员变量的定义和初始化应该在类定义之外,而不是在类定义内部。

 

 

实验任务2:

 1 #ifndef COMPLEX_H  
 2 
 3 #define COMPLEX_H  
 4 
 5 
 6 
 7 #include <iostream>  
 8 
 9 #include <cmath>  
10 
11 
12 
13 class Complex {  
14 
15 public:  
16 
17     static const std::string doc;   
18 
19 
20 
21 
22 
23     Complex(); 
24 
25     Complex(double real); // 只指定实部  
26 
27     Complex(double real, double imag); // 指定实部和虚部  
28 
29     Complex(const Complex& other); 
30 
31 
32 
33     // 获取实部和虚部  
34 
35     double get_real() const;  
36 
37     double get_imag() const;  
38 
39 
40 
41     // 复数加法  
42 
43     void add(const Complex& other);  
44 
45 
46 
47     // 友元函数声明  
48 
49     friend Complex add(const Complex& c1, const Complex& c2);  
50 
51     friend bool is_equal(const Complex& c1, const Complex& c2);  
52 
53     friend bool is_not_equal(const Complex& c1, const Complex& c2);  
54 
55     friend void output(const Complex& c);  
56 
57     friend double abs(const Complex& c);  
58 
59 
60 
61 private:  
62 
63     double real; // 实部  
64 
65     double imag; // 虚部  
66 
67 };  
68 
69 
70 
71 #endif // COMPLEX_H 
Complex.h
#include "Complex.h"  



// 描述信息  

const std::string Complex::doc = "a simplified complex class";  



// 默认构造函数  

Complex::Complex() : real(0), imag(0) {}  



// 只指定实部的构造函数  

Complex::Complex(double real) : real(real), imag(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;  

}  



// 友元函数实现  

Complex add(const Complex& c1, const Complex& c2) {  

    return Complex(c1.real + c2.real, c1.imag + c2.imag);  

}  



bool is_equal(const Complex& c1, const Complex& c2) {  

    return (c1.real == c2.real) && (c1.imag == c2.imag);  

}  



bool is_not_equal(const Complex& c1, const Complex& c2) {  

    return !is_equal(c1, c2);  

}  



void output(const Complex& c) {  

    std::cout << c.real << (c.imag >= 0 ? " + " : " - ") << std::abs(c.imag) << "i";  

}  



double abs(const Complex& c) {  

    return std::sqrt(c.real * c.real + c.imag * c.imag);  

}  
Complex.cpp
 1 #include <iostream>  
 2 
 3 #include "Complex.h"  
 4 
 5 
 6 
 7 using std::cout;  
 8 
 9 using std::endl;  
10 
11 using std::boolalpha;  
12 
13 
14 
15 void test() {  
16 
17     cout << "类成员测试: " << endl;  
18 
19     cout << Complex::doc << endl;  
20 
21     cout << endl;  
22 
23 
24 
25     cout << "Complex对象测试: " << endl;  
26 
27     Complex c1;  
28 
29     Complex c2(3, -4);  
30 
31     const Complex c3(3.5);  
32 
33     Complex c4(c3);  
34 
35 
36 
37     cout << "c1 = "; output(c1); cout << endl;  
38 
39     cout << "c2 = "; output(c2); cout << endl;  
40 
41     cout << "c3 = "; output(c3); cout << endl;  
42 
43     cout << "c4 = "; output(c4); cout << endl;  
44 
45     cout << "c4.real = " << c4.get_real() << ", c4.imag = " << c4.get_imag() << endl;  
46 
47 
48 
49     cout << endl;  
50 
51     cout << "复数运算测试: " << endl;  
52 
53     cout << "abs(c2) = " << abs(c2) << endl;  
54 
55     c1.add(c2);  
56 
57     cout << "c1 += c2, c1 = "; output(c1); cout << endl;  
58 
59     cout << boolalpha;  
60 
61     cout << "c1 == c2 : " << is_equal(c1, c2) << endl;  
62 
63     cout << "c1 != c3 : " << is_not_equal(c1, c3) << endl;  
64 
65     c4 = add(c2, c3);  
66 
67     cout << "c4 = c2 + c3, c4 = "; output(c4); cout << endl;  
68 
69 }  
70 
71 
72 
73 int main() {  
74 
75     test();  
76 
77     return 0;  
78 
79 }  
main.cpp

 

实验任务3:

 1 #include <iostream>
 2   #include <complex>
 3   
 4   using std::cout;
 5   using std::endl;
 6   using std::boolalpha;
 7   using std::complex;
 8   
 9   void test() {
10      cout << "标准库模板类comple测试: " << endl;
11      complex<double> c1;
12      complex<double> c2(3, -4);
13      const complex<double> c3(3.5);
14      complex<double> c4(c3);
15  
16      cout << "c1 = " << c1 << endl;
17      cout << "c2 = " << c2 << endl;
18      cout << "c3 = " << c3 << endl;
19      cout << "c4 = " << c4 << endl;
20      cout << "c4.real = " << c4.real() << ", c4.imag = " << c4.imag() << endl;
21      cout << endl;
22  
23      cout << "复数运算测试: " << endl;     
24      cout << "abs(c2) = " << abs(c2) << endl;
25      c1 += c2;
26      cout << "c1 += c2, c1 = " << c1 << endl;
27      cout << boolalpha;
28      cout << "c1 == c2 : " << (c1 == c2) << endl;
29      cout << "c1 != c3 : " << (c1 != c3) << endl;
30      c4 = c2 + c3;
31      cout << "c4 = c2 + c3, c4 = " << c4 << endl;
32  }
33  
34  int main() {
35      test();
36  }
task3.cpp

 

接口:

- `complex<double> c1` 使用默认构造函数创建了一个复数对象 c1。
- `complex<double> c2(3, -4)` 使用初始化列表构造一个复数对象 c2。
- `const complex<double> c3(3.5)` 创建一个常量复数对象 c3。
- `complex<double> c4(c3)` 利用复制构造函数生成复数对象 c4。

可以直接使用 `std::cout` 和 `<<` 运算符输出复数对象。
`c4.real()` 和 `c4.imag()` 用来分别获取复数对象的实部和虚部。
`c1 += c2` 使用 `+=` 运算符将 c2 加到 c1 上。
`(c1 == c2)` 使用 `==` 运算符比较两个复数对象的相等性。
`(c1 != c3)` 使用 `!=` 运算符判断两个复数对象是否不相等。
`abs(c2)` 计算复数对象 c2 的模长。
`c4 = c2 + c3` 使用 `+` 运算符执行两个复数对象的相加。
写法更简洁之处:
1. 构造函数:
- 任务2:须手动定义多种构造函数以初始化复数对象。
- 任务3:可以直接使用列表初始化或特定构造函数来创建复数对象,代码更加简化。
2. 运算:
- 任务2:需要手动实现加、减、乘、除等运算符的重载。
- 任务3:标准库的 complex 类已重载基本运算符,使复数运算更加直接。
3. 访问:
- 任务2:必须通过成员函数(如 get_real() 和 get_imag())获取实部和虚部。
- 任务3:可以直接使用 `.real()` 和 `.imag()` 成员方法来访问复数的实部和虚部,代码看起来更清晰。
4. 输出:
- 任务2:需要自定义一个 output() 函数来格式化输出复数。
- 任务3:可以直接利用 `std::cout` 和 `<<` 运算符输出复数。
5. 模运算:
- 任务2:需手动实现复数的模运算。
- 任务3:可以直接调用标准库中的 `abs()` 函数来计算复数的模。
启发:
标准库 `complex` 类的接口设计简单明了,易于理解与使用,从而使代码更为简洁与直观。
 

实验任务4:

 1 #pragma once
 2 #include <string>
 3   
 4 class Fraction {
 5 public:
 6         Fraction(int up, int down = 1);
 7         Fraction(const Fraction&);
 8     
 9             int get_up() const;
10         int get_down() const;
11          Fraction negative() const;
12          int gcd()const;
13         friend Fraction add(const Fraction&, const Fraction&);
14         friend Fraction sub(const Fraction&, const Fraction&);
15          friend Fraction mul(const Fraction&, const Fraction&);
16          friend Fraction div(const Fraction&, const Fraction&);
17          friend void output(const Fraction&);
18      
19          
20      static const std::string doc;
21      
22 private:
23         int up;
24          int down;
25      
26  };
Fraction.h
 1   #include "Fraction.h"
 2   #include <iostream>
 3    #include<cmath>
 4    const std::string Fraction::doc = "Fraction类v0.01版. 目前仅支持分数对象的构造、输出、加/减/乘/除运算";
 5   
 6   Fraction::Fraction(int up, int down) : up(up), down(down) {}
 7   
 8   Fraction::Fraction(const Fraction & other) : up(other.up), down(other.down) {}
 9   
10   int Fraction::get_up() const {
11       int a = gcd();
12           return up/a;
13      
14  }
15  
16   int Fraction::get_down() const {
17       int a = gcd();
18       return down/a;
19      
20  }
21  
22   Fraction Fraction::negative() const {
23           return Fraction(-up, down);
24      
25  }
26  
27   Fraction add(const Fraction & f1, const Fraction & f2) {
28          return Fraction(f1.up * f2.down + f2.up * f1.down, f1.down * f2.down);
29      
30  }
31  
32   Fraction sub(const Fraction & f1, const Fraction & f2) {
33           return Fraction(f1.up * f2.down - f2.up * f1.down, f1.down * f2.down);
34      
35  }
36  
37   Fraction mul(const Fraction & f1, const Fraction & f2) {
38          return Fraction(f1.up * f2.up, f1.down * f2.down);
39      
40  }
41  
42   Fraction div(const Fraction & f1, const Fraction & f2) {
43           return Fraction(f1.up * f2.down, f1.down * f2.up);
44      
45  }
46  
47   void output(const Fraction & f) {
48           int a = f.down, b = f.up;
49           int c = a, d = b;
50           int temp;
51           while (d != 0)
52               {
53                   temp = c % d;
54                   c = d;
55                   d = temp;
56               }
57           int gcd = c;
58           b /= gcd;
59           a /= gcd;
60           if (a == 0)
61           {
62               std::cout << "分母不能为0";
63               return;
64           }
65           if (a < 0) {
66                   b = -b;
67                   a = -a;
68          
69      }
70          if (b == 0) {
71                   std::cout << "0";
72          
73      }
74      else if (a == 1) {
75                   std::cout << b;
76          
77     }
78      else {
79                   std::cout << b << "/" << a;
80          
81      }
82      
83  }
84   int Fraction::gcd()const 
85   {
86       int c = up;
87       int d = down;
88       int temp;
89       while (d != 0)
90       {
91           temp = c % d;
92           c = d;
93           d = temp;
94       }
95       return c;
96   }
Fraction.cpp
 1 #include "Fraction.h"
 2  #include <iostream>
 3 
 4  using std::cout;
 5  using std::endl;
 6 
 7 
 8  void test1() {
 9  cout << "Fraction类测试: " << endl;
10  cout << Fraction::doc << endl << endl;
11 
12  Fraction f1(5);
13  Fraction f2(3, -4), f3(-18, 12);
14  Fraction f4(f3);
15  cout << "f1 = "; output(f1); cout << endl;
16  cout << "f2 = "; output(f2); cout << endl;
17  cout << "f3 = "; output(f3); cout << endl;
18  cout << "f4 = "; output(f4); cout << endl;
19 
20  Fraction f5(f4.negative());
21  cout << "f5 = "; output(f5); cout << endl;
22  cout << "f5.get_up() = " << f5.get_up() << ", f5.get_down() = " << f5.get_down()<< endl ;
23 
24  cout << "f1 + f2 = "; output(add(f1, f2)); cout << endl;
25  cout << "f1 - f2 = "; output(sub(f1, f2)); cout << endl;
26  cout << "f1 * f2 = "; output(mul(f1, f2)); cout << endl;
27  cout << "f1 / f2 = "; output(div(f1, f2)); cout << endl;
28  cout << "f4 + f5 = "; output(add(f4, f5)); cout << endl;
29  }
30 
31  void test2() {
32  Fraction f6(42, 55), f7(0, 3);
33  cout << "f6 = "; output(f6); cout << endl;
34  cout << "f7 = "; output(f7); cout << endl;
35  cout << "f6 / f7 = "; output(div(f6, f7)); cout << endl;
36 }
37 
38 int main() {
39  cout << "测试1: Fraction类基础功能测试\n";
40  test1();
41 
42  cout << "\n测试2: 分母为0测试: \n";
43  test2();
44  }
main.cpp

 

 

实验任务5:

 

 #ifndef _ _ACCOUNT_H_ _
  #define _ _ACCOUNT_H_ _
  class SavingsAccount{
  private:
      int id;
      double balance;
      double rate;
      int lastDate;
      double accumulation;
     static double total;
     void record(int date,double amount);
     double accumulate(int date)const{
         return accumulation+balance*(date-lastDate);
     }
 public:
     SavingsAccount(int date,int id,double rate);
     int getId()const{return id;}
     double getBalance()const{return balance;}
     double getRate()const{return rate;}
     static double getTotal(){return total;}
     void deposit(int date,double amount);
     void withdraw(int date,double amount);
    void settle(int date);
     void show()const;
     
 };
 #endif//_ _ACCOUNT_H_ _
Account.h
 1  #include"Account.h"
 2   #include<cmath>
 3   #include<iostream>
 4   using namespace std;
 5   double SavingsAccount::total=0;
 6   SavingsAccount::SavingsAccount(int date,int id,double rate)
 7   :id(id),balance(0),rate(rate),lastDate(date),accumulation(0){
 8       cout<<date<<"\t#"<<id<<" is created"<<endl;
 9   } 
10  void  SavingsAccount::record(int date,double amount){
11      accumulation=accumulate(date);
12      lastDate=date;
13      amount=floor(amount*100+0.5)/100;
14      balance+=amount;
15      total+=amount;
16      cout<<date<<"\t#"<<id<<"\t"<<amount<<"\t"<<balance<<endl;
17  }
18  void  SavingsAccount::deposit(int date,double amount){
19      record(date,amount);
20  }
21  void  SavingsAccount::withdraw(int date,double amount){
22      if(amount>getBalance())
23          cout<<"Error:not enough money"<<endl;
24      else
25          record(date,-amount);
26  }
27  void  SavingsAccount::settle(int date){
28      double interest=accumulate(date)*rate/365;
29      if(interest!=0)
30          record(date,interest);
31          accumulation=0;
32  }
33  void SavingsAccount::show()const{
34      cout<<"#"<<id<<"\tBalance:"<<balance;
35  }
Account.cpp
 1  #include"Account.h"
 2   #include<iostream>
 3   using namespace std;
 4  int main()
 5   {
 6        SavingsAccount sa0(1,21325302,0.015);
 7        SavingsAccount sa1(1,58320212,0.015);
 8        sa0.deposit(5,5000);
 9        sa1.deposit(25,10000);
10       sa0.deposit(45,5500);
11       sa1.withdraw(60,4000);
12       sa0.settle(90);
13       sa1.settle(90);
14       sa0.show();cout<<endl;
15       sa1.show();cout<<endl;
16       cout<<"Total:"<< SavingsAccount::getTotal()<<endl;
17       return 0;
18  }
main.cpp

 

 

改进建议:

1. 可以将金额的四舍五入逻辑提取到一个独立的工具函数中,以提高代码的可重用性。

2. 考虑使用更具可读性的输出格式。例如,采用统一的格式来输出消息,可以增强信息的清晰度。

3. 增加一个专门的方法来获取当前余额,这样就不需要再调用显示方法。

4. 尽量为那些不改变类状态的方法加上 const 修饰符,例如 accumulate() 和 getBalance() 方法,因为它们不会产生副作用。

5. 如果引入了常量(例如每日利息计算),可以考虑将其定义为类中的常量。

 

 

实验总结:
反思与改进

1.在实现过程中,我发现自己在类设计时有时会忽略数据的保护机制,未来需要更加注重访问权限的设置。

2.对于复杂的类和功能,我意识到需要提前进行详细的设计和规划,以避免在实现过程中出现逻辑混乱。

3.在使用标准库时,我会更加关注其提供的功能,合理利用现有资源,提高开发效率。

总结
本次实验不仅让我掌握了C++中的类和对象的基本用法,还培养了我在实际问题中运用面向对象设计思维的能力。通过不断的实践和反思,我对编程的理解更加深入,也为今后的学习和工作打下了坚实的基础。
posted @ 2024-10-28 17:51  nightlight  阅读(6)  评论(0编辑  收藏  举报