实验3

实验任务1:

 1 #pragma once  
 2 #include <iostream>  
 3 #include <string>  
 4 using std::string;  
 5 using std::cout;  
 6 
 7 // 按钮类  
 8 class Button {  
 9 public:  
10     Button(const string &text);  
11     string get_label() const;  
12     void click();  
13 private:  
14     string label;  
15 };  
16 
17 Button::Button(const string &text): label{text} {}  
18 
19 inline string Button::get_label() const {  
20     return label;  
21 }  
22 
23 void Button::click() {  
24     cout << "Button '" << label << "' clicked\n";  
25 }  
button.hpp
 1 #pragma once  
 2 #include "button.hpp"  
 3 #include <vector>  
 4 #include <iostream>  
 5 using std::vector;  
 6 using std::cout;  
 7 using std::endl;  
 8 
 9 // 窗口类  
10 class Window {  
11 public:  
12     Window(const string &win_title);  
13     void display() const;  
14     void close();  
15     void add_button(const string &label);  
16 private:  
17     string title;  
18     vector<Button> buttons;  
19 };  
20 
21 Window::Window(const string &win_title): title{win_title} {  
22     buttons.push_back(Button("close"));  
23 }  
24 
25 inline void Window::display() const {  
26     string s(40, '*');  
27     cout << s << endl;  
28     cout << "window title: " << title << endl;  
29     cout << "It has " << buttons.size() << " buttons: " << endl;  
30     for(const auto &i: buttons)  
31         cout << i.get_label() << " button" << endl;  
32     cout << s << endl;  
33 }  
34 
35 void Window::close() {  
36     cout << "close window '" << title << "'" << endl;  
37     buttons.at(0).click();  
38 }  
39 
40 void Window::add_button(const string &label) {  
41     buttons.push_back(Button(label));  
42 }  
window.hpp
 1 #include "window.hpp"  
 2 #include <iostream>  
 3 using std::cout;  
 4 using std::cin;  
 5 
 6 void test() {  
 7     Window w1("new window");  
 8     w1.add_button("maximize");  
 9     w1.display();  
10     w1.close();  
11 }  
12 
13 int main() {  
14     cout << "用组合类模拟简单GUI:\n";  
15     test();  
16 }  
task1.cpp

 问题1:

1. 自定义的类:
Button 类
Window 类

2. 使用到的标准库类:
std::string:用于处理字符串。
std::vector:用于存储 `Button` 对象的动态数组。
std::cout:用于输出信息到控制台。

3. 组合关系:

window---{string,button vector}

button----{string}

问题2:

回答:
1. 适合添加 const 的成员函数:

button 中click可以加const,因为它没改变类的成员。

Window中的close 也可以加const.

 

2. 适合设置成 inline 的成员函数:
Button 类中的click函数可以考虑设置为 inline,因为它是一个简单的函数,通常会在头文件中定义以提高效率。
Window 类中的 display() 函数可以考虑设置为 inline,如果它的实现较简单且频繁调用。

问题3:

回答:
功能是创建一个有40个‘*’的字符串,美化运行窗口,增强终端可读性和易用性

 

实验任务2:

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 void output1(const vector<int> &v) {
 5 for(auto &i: v)
 6 cout << i << ", ";
 7 cout << "\b\b \n";
 8 }
 9 void output2(const vector<vector<int>> v) {
10 for(auto &i: v) {
11 for(auto &j: i)
12 cout << j << ", ";
13 cout << "\b\b \n";
14  }
15 }
16 void test1() {
17 vector<int> v1(5, 42);
18 const vector<int> v2(v1);
19 v1.at(0) = -999;
20 cout << "v1: "; output1(v1);
21 cout << "v2: "; output1(v2);
22 cout << "v1.at(0) = " << v1.at(0) << endl;
23 cout << "v2.at(0) = " << v2.at(0) << endl;
24 }
25 void test2() {
26 vector<vector<int>> v1{{1, 2, 3}, {4, 5, 6, 7}};
27 const vector<vector<int>> v2(v1);
28 v1.at(0).push_back(-999);
29 cout << "v1: \n"; output2(v1);
30 cout << "v2: \n"; output2(v2);
31 vector<int> t1 = v1.at(0);
32 cout << t1.at(t1.size()-1) << endl;
33 const vector<int> t2 = v2.at(0);
34 cout << t2.at(t2.size()-1) << endl;
35 }
36 int main() {
37 cout << "测试1:\n";
38 test1();
39 cout << "\n测试2:\n";
40 test2();
41 }
task2.cpp

 问题1:

vector<int> v1(5, 42);:创建一个包含5个元素,值为42的向量。

const vector<int> v2(v1);:使用v1初始化v2,进行深复制,v2的内容与v1相同,但它们是独立的。

v1.at(0) = -999;:修改v1的第一个元素为-999。

问题2:

测试2模块中,这三行代码的功能分别是:

vector<vector<int>> v1{{1, 2, 3}, {4, 5, 6, 7}};:创建一个二维向量,包含两个子向量。

const vector<vector<int>> v2(v1);:深复制v1到v2,v2的内容与v1相同。

v1.at(0).push_back(-999);:在v1的第一个子向量中添加元素-999。

问题3:

测试2模块中,这四行代码的功能分别是:

vector<int> t1 = v1.at(0);:定义一个整数容器t1,将v1中索引为0的容器的元素赋给它;

cout << t1.at(t1.size()-1)<<endl;:输出t1中最后一个元素并换行;

const vector<int> t2 = v2.at(0);:获取v2的第一个子向量并赋值给t2。

cout << t2.at(t2.size()-1) << endl;:输出t2的最后一个元素。

问题4:

根据执行结果,反向分析、推断:

① 标准库模板类vector内部封装的复制构造函数,其实现机制是深复制。

② 是,模板类vector的接口at(),确实至少需要提供一个const成员函数作为接口,以便在需要只读访问时使用。

 

实验任务3:

 1 #pragma once  
 2 #include <iostream>  
 3 #include <cassert>  
 4 using std::cout;  
 5 using std::endl;  
 6 
 7 // 动态int数组对象类  
 8 class vectorInt {  
 9 public:  
10     vectorInt(int n); // 构造函数,指定大小  
11     vectorInt(int n, int value); // 构造函数,指定大小并初始化  
12     vectorInt(const vectorInt &vi); // 复制构造函数  
13     ~vectorInt(); // 析构函数  
14 
15     int& at(int index); // 返回指定索引的元素引用  
16     const int& at(int index) const; // 返回指定索引的元素常量引用  
17     vectorInt& assign(const vectorInt &v); // 赋值操作  
18     int get_size() const; // 返回数组大小  
19 
20 private:  
21     int size; // 数组大小  
22     int *ptr; // 指向动态数组的指针  
23 };  
24 
25 // 构造函数实现  
26 vectorInt::vectorInt(int n) : size{n}, ptr{new int[size]} {  
27 }  
28 
29 vectorInt::vectorInt(int n, int value) : size{n}, ptr{new int[size]} {  
30     for (auto i = 0; i < size; ++i)  
31         ptr[i] = value;  
32 }  
33 
34 vectorInt::vectorInt(const vectorInt &vi) : size{vi.size}, ptr{new int[size]} {  
35     for (auto i = 0; i < size; ++i)  
36         ptr[i] = vi.ptr[i];  
37 }  
38 
39 vectorInt::~vectorInt() {  
40     delete[] ptr; // 释放动态数组内存  
41 }  
42 
43 const int& vectorInt::at(int index) const {  
44     assert(index >= 0 && index < size);  
45     return ptr[index];  
46 }  
47 
48 int& vectorInt::at(int index) {  
49     assert(index >= 0 && index < size);  
50     return ptr[index];  
51 }  
52 
53 vectorInt& vectorInt::assign(const vectorInt &v) {  
54     delete[] ptr; // 释放原有内存  
55     size = v.size;  
56     ptr = new int[size];  
57     for (int i = 0; i < size; ++i)  
58         ptr[i] = v.ptr[i];  
59     return *this;  
60 }  
61 
62 int vectorInt::get_size() const {  
63     return size;  
64 }  
vectorInt.hpp
 1 #include "vectorInt.hpp"  
 2 #include <iostream>  
 3 using std::cin;  
 4 using std::cout;  
 5 
 6 void output(const vectorInt &vi) {  
 7     for (auto i = 0; i < vi.get_size(); ++i)  
 8         cout << vi.at(i) << ", ";  
 9     cout << "\b\b \n"; // 删除最后一个逗号  
10 }  
11 
12 void test1() {  
13     int n;  
14     cout << "Enter n: ";  
15     cin >> n;  
16     vectorInt x1(n); // 创建大小为n的vectorInt对象  
17     for (auto i = 0; i < n; ++i)  
18         x1.at(i) = i * i; // 初始化数据  
19     cout << "x1: "; output(x1);  
20 
21     vectorInt x2(n, 42); // 创建大小为n,所有元素初始化为42的vectorInt对象  
22     vectorInt x3(x2); // 使用x2创建x3,调用复制构造函数  
23 
24     x2.at(0) = -999; // 修改x2的第一个元素  
25     cout << "x2: "; output(x2);  
26     cout << "x3: "; output(x3); // x3应保持不变  
27 }  
28 
29 void test2() {  
30     const vectorInt x(5, 42); // 创建大小为5,所有元素初始化为42的vectorInt对象  
31     vectorInt y(10, 0); // 创建大小为10,所有元素初始化为0的vectorInt对象  
32     cout << "y: "; output(y);  
33     y.assign(x); // 将x的值赋给y  
34     cout << "y: "; output(y);  
35     cout << "x.at(0) = " << x.at(0) << endl; // 输出x的第一个元素  
36     cout << "y.at(0) = " << y.at(0) << endl; // 输出y的第一个元素  
37 }  
38 
39 int main() {  
40     cout << "测试1: \n";  
41     test1();  
42     cout << "\n测试2: \n";  
43     test2();  
44 }  
task3.cpp

 

 问题1:复制构造函数实现为深复制。它通过分配新的内存并将原对象的内容逐个复制到新对象中,确保每个对象都有自己的独立数据。

问题2:不能运行,报错截图如下

因为返回值不是引用类型的话,就无法完成修改成员值的操作,所以会报错。有潜在安全隐患。可能会被错误调用。

问题3:assign()接口的返回值类型可以改成vectorInt,但不如使用assign()接口。返回引用(vectorInt&)可以支持链式调用,而返回值会导致对象的拷贝,增加不必要的开销。因此,返回引用更好。

 

实验任务4:

 1 #pragma once
 2   #include <iostream>
 3  #include <cassert>
 4   using std::cout;
 5   using std::endl;
 6   
 7   // 类Matrix的声明
 8  class Matrix {
 9  public:
10      Matrix(int n, int m);           // 构造函数,构造一个n*m的矩阵, 初始值为value
11      Matrix(int n);                  // 构造函数,构造一个n*n的矩阵, 初始值为value
12      Matrix(const Matrix& x);        // 复制构造函数, 使用已有的矩阵X构造
13      ~Matrix();
14  
15      void set(const double* pvalue);         // 用pvalue指向的连续内存块数据按行为矩阵赋值
16      void clear();                           // 把矩阵对象的值置0
17  
18      const double& at(int i, int j) const;   // 返回矩阵对象索引(i,j)的元素const引用
19      double& at(int i, int j);               // 返回矩阵对象索引(i,j)的元素引用
20  
21      int get_lines() const;                  // 返回矩阵对象行数
22      int get_cols() const;                   // 返回矩阵对象列数
23  
24      void display() const;                    // 按行显示矩阵对象元素值
25  
26  private:
27     int lines;      // 矩阵对象内元素行数
28      int cols;       // 矩阵对象内元素列数
29      double* ptr;
30  };
31  
32  Matrix::Matrix(int n, int m) : lines{ n }, cols{ m }, ptr{ new double[lines * cols] } {}
33  Matrix::Matrix(int n) : lines{ n }, cols{ n }, ptr{ new double[lines * cols] } {}
34  Matrix::Matrix(const Matrix& x) : lines{ x.lines }, cols{ x.cols }, ptr{ new double[lines * cols] } {
35      for (int i = 0; i < lines * cols; ++i) {
36          ptr[i] = x.ptr[i];
37      }
38  }
39  Matrix::~Matrix() {
40      delete []ptr;     
41  }
42  
43  
44  void Matrix::set(const double* pvalue) {
45      for (int i = 0; i < lines; ++i) {
46          for (int j = 0; j < cols; ++j) {
47              ptr[i * cols + j] = pvalue[i * cols + j];
48          }
49      }
50  }
51  void Matrix::clear() {
52      for (int i = 0; i < lines * cols; ++i) {
53          ptr[i] = 0;
54      }
55  }
56  const double& Matrix::at(int i, int j) const {
57      if(i >= 0 && i < lines && j >= 0 && j < cols)
58      return ptr[i * cols + j];
59  }
60  double& Matrix::at(int i, int j) {
61      if(i >= 0 && i < lines && j >= 0 && j < cols)
62      return ptr[i * cols + j];
63  }
64  int Matrix::get_lines() const { return lines; }
65  int Matrix::get_cols() const { return cols; }
66  void Matrix::display() const {
67      for (int i = 0; i < lines; ++i) {
68          for (int i1 = 0; i1 < cols; ++i1) {
69              cout << ptr[i * cols + i1] << ", ";
70          }
71          cout << "\b\b \n";
72      }
73  }
matrix.hpp
 1  #include "matrix.hpp"
 2  #include <iostream>
 3  #include <cassert>
 4  
 5  using std::cin;
 6   using std::cout;
 7   using std::endl;
 8  
 9   
10  const int N = 1000;
11  
12  // 输出矩阵对象索引为index所在行的所有元素
13  void output(const Matrix &m, int index) {
14      assert(index >= 0 && index < m.get_lines());
15  
16      for(auto j = 0; j < m.get_cols(); ++j)
17          cout << m.at(index, j) << ", ";
18     cout << "\b\b \n";
19  }
20  
21  
22  void test1() {
23      double x[1000] = {1,3,5,2,4,6,7,8,9};
24  
25      int n, m;
26      cout << "Enter n and m: ";
27      cin >> n >> m;
28  
29      Matrix m1(n, m);    // 创建矩阵对象m1, 大小n×m
30      m1.set(x);          // 用一维数组x的值按行为矩阵m1赋值
31  
32      Matrix m2(m, n);    // 创建矩阵对象m2, 大小m×n
33      m2.set(x);          // 用一维数组x的值按行为矩阵m1赋值
34  
35      Matrix m3(2);       // 创建一个2×2矩阵对象
36      m3.set(x);          // 用一维数组x的值按行为矩阵m4赋值
37  
38      cout << "矩阵对象m1: \n";   m1.display();  cout << endl;
39      cout << "矩阵对象m2: \n";   m2.display();  cout << endl;
40      cout << "矩阵对象m3: \n";   m3.display();  cout << endl;
41  }
42  
43  void test2() {
44      Matrix m1(2, 3);
45      m1.clear();
46      
47      const Matrix m2(m1);
48      m1.at(0, 0) = -999;
49  
50      cout << "m1.at(0, 0) = " << m1.at(0, 0) << endl;
51      cout << "m2.at(0, 0) = " << m2.at(0, 0) << endl;
52      cout << "矩阵对象m1第0行: "; output(m1, 0);
53      cout << "矩阵对象m2第0行: "; output(m2, 0);
54  }
55 
56  int main() {
57      cout << "测试1: \n";
58      test1();
59  
60      cout << "测试2: \n";
61      test2();
62  }
task4.cpp

 

运行结果截图:

 

 

实验任务5:

 1  #pragma once
 2   #include <iostream>
 3   #include <string>
 4  #include <vector>
 5   
 6   using std::string;
 7   using std::cout;
 8   using std::endl;
 9   using std::vector;
10  
11  class User {
12 public:
13      User(const string& name, const string& password = "123456", const string& email = "");
14      void set_email();
15      void change_password();
16      void display();
17  private:
18      string name;
19      string password;
20      string email;
21  };
22  
23  User::User(const string& name, const string& password, const string& email) : name{ name }, password{ password }, email{ email } {}
24  
25  void User::set_email() {
26      string input;
27      bool c = false;
28      cout << "Enter email address: ";
29      do {
30          
31          std::cin >> input;
32          size_t pos = input.find('@');
33          if (pos != string::npos) {
34              c = true;
35          }
36          else {
37              cout << "illegal email. Please re-enter email: ";
38          }
39      } while (!c);
40      email = input;
41      cout << "email is set successfully..." << endl;
42  }
43  
44  void User::change_password() {
45      string old_password;
46      string new_password;
47      int cnt = 0;
48      bool correct = false;
49      cout << "Enter old password: ";
50      do {
51          
52          std::cin >> old_password;
53          if (old_password == password) {
54              correct = true;
55          }
56          else {
57              cnt++;
58              if (cnt == 3) {
59                  cout << "password input error. Please try after a while." << endl;
60                  return;
61              }
62              cout << "password input error. Please re-enter again: ";
63          }
64      } while (!correct);
65      cout << "Enter new password: ";
66      std::cin >> new_password;
67      password = new_password;
68      cout << "new password is set successfully..." << endl;
69  }
70  
71  void User::display() {
72      cout << "name: " << name << endl;
73      string showpassword(password.length(), '*');
74      cout << "pass: " << showpassword << endl;
75      cout << "email: " << email << endl;
76  }
user.cpp
 1  #include "user.hpp"
 2   #include <iostream>
 3   #include <vector>
 4   #include <string>
 5   
 6   using std::cin;
 7   using std::cout;
 8   using std::endl;
 9   using std::vector;
10  using std::string;
11  
12  void test() {
13      vector<User> user_lst;
14  
15      User u1("Alice", "2024113", "Alice@hotmail.com");
16      user_lst.push_back(u1);
17      cout << endl;
18  
19      User u2("Bob");
20      u2.set_email();
21      u2.change_password();
22      user_lst.push_back(u2);
23      cout << endl;
24       
25      User u3("Hellen");
26      u3.set_email();
27      u3.change_password();
28      user_lst.push_back(u3);
29      cout << endl;
30  
31      cout << "There are " << user_lst.size() << " users. they are: " << endl;
32      for(auto &i: user_lst) {
33          i.display();
34          cout << endl;
35      }
36  }
37  
38  int main() {
39      test();
40  }
task5.cpp

实验结果:

 

实验任务6:

 1  #include"account.h"
 2  #include<iostream>
 3   
 4   using namespace std;
 5   
 6   int main() 
 7   {
 8       Date date(2008, 11, 1);
 9   
10      SavingsAccount accounts[] = 
11      {
12      SavingsAccount(date, "03755217", 0.015),
13      SavingsAccount(date, "02342342", 0.015)
14      };
15      const int n = sizeof(accounts) / sizeof(SavingsAccount);
16  
17      accounts[0].deposit(Date(2008, 11, 5), 5000, "salary");
18      accounts[1].deposit(Date(2008, 11, 25), 10000, "sell stock 0323");
19      accounts[0].deposit(Date(2008, 12, 5), 5500, "salary");
20      accounts[1].withdraw(Date(2008, 12, 20), 4000, "buy a laptop");
21  
22      cout << endl;
23      for (int i = 0; i < n; i++) 
24      {
25          accounts[i].settle(Date(2009, 1, 1));
26          accounts[i].show();
27          cout << endl;
28      }
29      cout << "Total: " << SavingsAccount::getTotal() << endl;
30      return 0;
31  }
6_25
 #include "account.h"
  #include<cmath>
  #include<iostream>
  
  using namespace std;
  
  double SavingsAccount::total = 0;
  
  SavingsAccount::SavingsAccount(const Date& date, const string& id, double rate) : id(id), balance(0), rate(rate), lastDate(date), accumulation(0) 
 {
     date.show();
     cout << "\t#" << id << "  created" << endl;
 }
 
 void SavingsAccount::record(const Date& date, double amount, const string& desc) 
 {
     accumulation = accumulate(date);
 
     lastDate = date;
 
     amount = floor(amount * 100 + 0.5) / 100; //保留小数点后两位
     balance += amount;
     total += amount;
     date.show();
 
     cout << "\t#" << id << "\t" << amount << "\t" << balance << "\t" << desc << endl;
 
 }
 void SavingsAccount::error(const string& msg)const 
 {
     cout << "Error(#" << id << "):" << msg << endl;
 
 }
 
 void SavingsAccount::deposit(const Date& date, double amount, const string& desc) 
 {
     record(date, amount, desc);
 
 }
 
 void SavingsAccount::withdraw(const Date& date, double amount, const string& desc) 
 {
     if (amount > getBalance())
         error("not enough money");
     else
         record(date, -amount, desc);
 
 }
 
 void SavingsAccount::settle(const Date& date) 
 {
 
    double interest = accumulate(date) * rate / date.distance(Date(date.getYear() - 1, 1, 1)); //计算年息
 
     if (interest != 0)
 
         record(date, interest, "interest");
 
     accumulation = 0;
 
 }
 
 void SavingsAccount::show() const 
 {
     cout << id << "\tBalance:" << balance;
 }
account.cpp
 1  #pragma once
 2   #include"date.h"
 3   #include<string>
 4   
 5   class SavingsAccount 
 6   {
 7   private:
 8       std::string id;
 9      double balance;
10      double rate;
11      Date  lastDate;
12      double accumulation;
13      static double total;
14      void record(const Date& date, double amount, const std::string& desc);
15  
16      void error(const std::string& msg)const;
17  
18      double accumulate(const Date& date) const 
19      {
20          return accumulation + balance * date.distance(lastDate);
21  
22      }
23  public:
24      SavingsAccount(const Date& date, const std::string& id, double rate); 
25      const std::string& getId() const { return id; }
26      double getBalance() const { return balance; }
27  
28      double getRate() const { return rate; }
29  
30      static double getTotal() { return total; }
31  
32      void deposit(const Date& date, double amount, const std::string& desc);
33  
34      void withdraw(const Date& date, double amount, const std::string& desc);
35  
36      void settle(const Date& date);
37  
38      void show() const;
39  };
account.h
 1  #include"date.h"
 2   #include<iostream>
 3   #include<cstdlib>
 4   
 5   using namespace std;
 6   
 7   namespace 
 8   {
 9       const int DAYS_BEFORE_MONTH[] = { 0,31,59,90,120,151,181,212,243,273,304,334,365 };
10  }
11  Date::Date(int year, int month, int day) :year(year), month(month), day(day) 
12  {
13      if (day <= 0 || day > getMaxDay()) 
14      {
15          cout << "Invalid date: ";
16          show();
17          cout << endl;
18          exit(1);
19      }
20      int years = year - 1;
21      totalDays = year * 365 + years / 4 - years / 100 + years / 400 + DAYS_BEFORE_MONTH[month - 1] + day;
22      if (isLeapYear() && month > 2) totalDays++;
23  }
24  int Date::getMaxDay()const 
25  {
26      if (isLeapYear() && month == 2)
27          return 29;
28      else
29          return DAYS_BEFORE_MONTH[month] - DAYS_BEFORE_MONTH[month - 1];
30  }
31  void Date::show() const 
32  {
33      cout << getYear() << "-" << getMonth() << "-" << getDay();
34  }
date.cpp
 1  #pragma once
 2   class Date 
 3   {
 4   private:
 5       int year;
 6       int month;
 7       int day;
 8       int totalDays;
 9   public:
10      Date(int year, int month, int day);
11      int getYear()const { return year; }
12      int getMonth() const { return month; }
13      int getDay()const { return day; }
14      int getMaxDay()const;
15      bool isLeapYear()const 
16      {
17          return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
18      }
19      void show()const;
20      int distance(const Date& date)const 
21      {
22          return totalDays - date.totalDays;
23      }
24  };
date.h

实验结果:

 

 改进建议:

  1. 使用this指针:在类的方法中,建议使用this指针来引用当前对象,以避免与参数或局部变量的名称混淆。

  2. 金钱处理类:在处理金钱相关的操作时,建议引入一个专门的金钱处理类,以避免由于浮点数精度问题导致的计算错误。

  3. 适当使用inline函数来提高程序运行效率

  4. 常量成员函数:对于不修改类成员变量的函数,建议将其声明为常量成员函数(使用const)。例如,getBalance()getId()等函数应标记为const,以明确其只读性质。

 

posted @ 2024-11-10 13:05  nightlight  阅读(2)  评论(0编辑  收藏  举报