實驗3 類和物件_基礎程式設計2

陈少秋發表於2024-11-11

實驗任務一

原始碼

實驗3 類和物件_基礎程式設計2
 1 #pragma once
 2 
 3 #include<iostream>
 4 #include<string>
 5 
 6 using std::string;
 7 using std::cout;
 8 
 9 //按鈕類
10 class Button {
11 public:
12     Button(const string& text);
13     string get_label()const;
14     void click();
15 
16 private:
17     string label;
18 };
19 
20 Button::Button(const string &text):label{text}{}
21 
22 inline string Button::get_label() const {
23     return label;
24 }
25 
26 void Button::click(){
27     cout << "Button" << label << "clicked\n";
28 }
Button.hpp
實驗3 類和物件_基礎程式設計2
 1 #pragma once
 2 #include"button.hpp"
 3 #include<vector>
 4 #include<iostream>
 5 
 6 using std::vector;
 7 using std::cout;
 8 using std::endl;
 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 
28     cout << s << endl;
29     cout << "window title:" << title << endl;
30     cout << "It has " << buttons.size() << " buttons: " << endl;
31     for (const auto& i : buttons) {
32         cout << i.get_label() << " button " << endl;
33     }
34     cout << s << endl;
35 }
36 
37 void window::close() {
38     cout << "close window '" << title << "'" << endl;
39     buttons.at(0).click();
40 }
41 
42 void window::add_button(const string &label) {
43     buttons.push_back(Button(label));
44 }
Window.hpp
實驗3 類和物件_基礎程式設計2
 1 #include "window.hpp"
 2 #include<iostream>
 3 
 4 using std::cout;
 5 using std::cin;
 6 
 7 void test() {
 8     window w1("new window");
 9     w1.add_button("maximize");
10     w1.display();
11     w1.close();
12 }
13 
14 int main() {
15     cout << "用組合類模擬簡單GUI:\n";
16     test();
17 }
task1.cpp

執行結果

問題1:這個模擬簡單GUI的示例程式碼中,自定義了幾個類?使用到了標準庫的哪幾個類?, 哪些類和類之間存在組合關係?

答:定義了Button和Window兩個類;使用到標準庫中的string,vector;sting定義了Button中label且在Window中有輸出,vector定義了Window中的Buttons,是一個string的vector。

問題2:在自定義類Button和Window中,有些成員函式定義時加了const, 有些設定成了 inline。如果你是類的設計者,目前那些沒有加const或沒有設定成inline的,適合新增const, 適合設定成inline嗎?你的思考依據是?

答:click()和close()都沒必要加const,因為其沒有引數傳入;它們也沒必要加inline,因為只在開頭和結尾使用一次。

  而add_button不能新增const,如果需要頻繁的新增button的話可以新增inline

問題3:類Window的定義中,有這樣一行程式碼:string s(40,'*'),其功能是?

答:使用string構造一個字串,第一個引數是字串長度int,第二個引數是每個位置應填充的字元;程式碼將創造含四十個連續*的字串

實驗任務二

原始碼

實驗3 類和物件_基礎程式設計2
 1 #include<iostream>
 2 #include<vector>
 3 
 4 using namespace std;
 5 
 6 void output1(const vector<int>& v) {
 7     for (auto& i : v)
 8         cout << i << ",";
 9     cout << "\b \n";
10 }
11 
12 void output2(const vector<vector<int>>v) {
13     for (auto& i : v)
14     {
15         for (auto& j : i)
16             cout << j << ",";
17         cout << "\b \n";
18     }
19 }
20 
21 void test1() {
22     vector<int>v1(5, 42);
23     const vector<int>v2(v1);
24 
25     v1.at(0) = -999;
26     cout << "v1:"; output1(v1);
27     cout << "v2: "; output1(v2);
28     cout << "v1.at(0) = " << v1.at(0) << endl;
29     cout << "v2.at(0) = " << v2.at(0) << endl;
30 }
31 
32 void test2() {
33     vector<vector<int>>v1{ {1,2,3},{4,5,6,7} };
34     const vector<vector<int>>v2(v1);
35 
36     v1.at(0).push_back(-999);
37     cout << "v1: \n"; output2(v1);
38     cout << "v2: \n"; output2(v2);
39 
40     vector<int>t1 = v1.at(0);
41     cout << t1.at(t1.size() - 1) << endl;
42 
43     const vector<int>t2 = v2.at(0);
44     cout << t2.at(t2.size() - 1) << endl;
45 }
46 
47 int main() {
48     cout << "測試1:\n";
49     test1();
50     cout << "\n測試2:\n";
51     test2();
52 }
task2.cpp

執行測試截圖

問題1:測試1模組中,這三行程式碼的功能分別是?

答:vector<int>v1(5, 42);創造名為v1的一維向量,用五個42填充

  const vector<int>v2(v1);創造一個名為v2的一維向量,用v2初始化它,有const修飾不可修改

  v1.at(0) = -999;用at成員函式訪問v1的第一個元素並將它改為-999

問題2:測試2模組中,這三行程式碼的功能分別是?

答:vector<vector<int>>v1{ {1,2,3},{4,5,6,7} };創造一個名為v1的二維向量,它包含兩個一維向量,分別是{1,2,3}和{4,5,6,7}

  const vector<vector<int>>v2(v1);創造一個名為v2的二維向量,用v1初始化它,用const修飾不可修改

  v1.at(0).push_back(-999);用at成員函式訪問v1的第一個一維向量,並向末尾新增-999

問題3:測試2模組中,這四行程式碼的功能分別是?

答:vector<int>t1 = v1.at(0);用 at 成員函式訪問 v1 中的第一個一維向量,並將其賦值給名為 t1 的一維向量

  cout << t1.at(t1.size() - 1) << endl;用 at 成員函式訪問 t1 中最後一個元素,並輸出它的值

  const vector<int>t2 = v2.at(0);用 at 成員函式訪問 v2中的第一個一維向量,並將其賦值給名為 t2 的一維向量

  cout << t2.at(t2.size() - 1) << endl;用 at 成員函式訪問 t2中最後一個元素,並輸出它的值

實驗任務三

原始碼

實驗3 類和物件_基礎程式設計2
 1 #pragma once
 2 
 3 #include<iostream>
 4 #include<cassert>
 5 
 6 using std::cout;
 7 using std::endl;
 8 
 9 class vectorInt {
10 public:
11     vectorInt(int n);
12     vectorInt(int n, int value);
13     vectorInt(const vectorInt& vi);
14     ~vectorInt();
15 
16     int& at(int index);
17     const int& at(int index)const;
18 
19     vectorInt& assign(const vectorInt& v);
20     int get_size() const;
21 
22 private:
23     int size;
24     int* ptr;
25 };
26 
27 vectorInt::vectorInt(int n) :size{ n }, ptr{ new int[size] }{}
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 
46     return ptr[index];
47 }
48 
49 int& vectorInt::at(int index) {
50     assert(index >= 0 && index < size);
51 
52     return ptr[index];
53 }
54 
55 vectorInt& vectorInt::assign(const vectorInt& v) {
56     delete[] ptr; //把物件原來的東西刪掉
57 
58     size = v.size;
59     ptr = new int[size];
60     
61     for (int i = 0; i < size; ++i)
62         ptr[i] = v.ptr[i];
63 
64     return *this;
65 }
66 
67 int vectorInt::get_size() const {
68     return size;
69 }
vectorInt.hpp
實驗3 類和物件_基礎程式設計2
 1 #include"vectorInt.hpp"
 2 #include<iostream>
 3 
 4 using std::cin;
 5 using std::cout;
 6 
 7 void output(const vectorInt& vi) {
 8     for (auto i = 0; i < vi.get_size(); ++i)
 9         cout << vi.at(i) << ",";
10     cout << "\b \n";
11 }
12 
13 void test1() {
14     int n;
15     cout << "Enter n:";
16     cin >> n;
17 
18     vectorInt x1(n);
19     for (auto i = 0; i < n; ++i)
20         x1.at(i) = i * i;
21     cout << "x1: "; output(x1);
22 
23     vectorInt x2(n, 42);
24     vectorInt x3(x2);
25     x2.at(0) = -999;
26     cout << "x2: "; output(x2);
27     cout << "x3: "; output(x3);
28 }
29 
30 void test2() {
31     const vectorInt x(5, 42);
32     vectorInt y(10, 0);
33 
34     cout << "y: "; output(y);
35     y.assign(x);
36     cout << "y: "; output(y);
37 
38     cout << "x.at(0) = " << x.at(0) << endl;
39     cout << "y.at(0) = " << y.at(0) << endl;
40 }
41 
42 int main() {
43     cout << "測試1: \n";
44     test1();
45 
46     cout << "\n測試2: \n";
47     test2();
48 }
task3.cpp

執行測試截圖

問題1:vectorInt類中,複製建構函式(line14)的實現,是深複製還是淺複製?

答:深複製,因為釋放了原有資源並且分配了新記憶體

問題2:vectorInt類中,這兩個at()介面,如果返回值型別改成int而非int&(相應地,實現部分也 同步修改),測試程式碼還能正確執行嗎?如果把line18返回值型別前面的const掉,針對這個測試 程式碼,是否有潛在安全隱患?嘗試分析說明。

答:(1)不能。

  (2)如果去掉第一個const(即返回型別變為int&),那麼返回的引用將不再是對常量的引用,呼叫者可以透過這個引用來修改整數值。這可能會破壞類的封裝性,特別是如果這個函式返回的是類的內部資料結構的直接引用的話。如果去掉第二個const(即函式宣告變為int& at(int index)),那麼該函式將不再是一個常量成員函式,它將能夠修改呼叫它的物件的狀態。這可能會導致在不應該修改物件狀態的情況下意外地修改了物件,從而引入難以追蹤的錯誤。

問題3:vectorInt類中,assign()介面,返回值型別可以改成vectorInt嗎?你的結論,及,原因分析。

答:不可以?執行結果沒有出錯,但是返回物件會帶來不必要的複製,而且如果錯誤返回物件的引用會導致未定義行為

實驗任務四

原始碼

實驗3 類和物件_基礎程式設計2
 1 #pragma once
 2 
 3 #include<iostream>
 4 #include<cassert>
 5 #include<cstring>
 6 
 7 using std::cout;
 8 using std::endl;
 9 
10 // 類Matrix的宣告
11 class Matrix {
12 public:
13     Matrix(int n, int m);    // 建構函式,構造一個n*m的矩陣, 初始值為value
14     Matrix(int n);            // 建構函式,構造一個n*n的矩陣, 初始值為value
15     Matrix(const Matrix& x);// 複製建構函式, 使用已有的矩陣X構造
16     ~Matrix();
17 
18     void set(const double* pvalue);// 用pvalue指向的連續記憶體塊資料按行為矩陣賦值
19     void clear(); //把矩陣物件的值置零
20     const double& at(int i, int j) const; // 返回矩陣物件索引(i,j)的元素const引用
21     double& at(int i, int j); // 返回矩陣物件索引(i,j)的元素引用
22     int get_lines() const; // 返回矩陣物件行數
23     int get_cols() const; // 返回矩陣物件列數
24     void display() const; // 按行顯示矩陣物件元素值
25 
26 private:
27     int lines;
28     int cols;
29     double* ptr;
30 };
31 
32 //建構函式
33 Matrix::Matrix(int n, int m) :lines{ n }, cols{ m }, ptr{ new double[lines*cols]} {}
34 Matrix::Matrix(int n) :Matrix(n, n) {};
35 Matrix::Matrix(const Matrix&x) :lines { x.lines }, cols{ x.cols}, ptr{ new double[x.lines * x.cols] } {
36     std::memcpy(ptr, x.ptr, sizeof(double) * x.lines * x.cols);//怎麼方便成這樣 :)
37 }
38 
39 //解構函式
40 Matrix::~Matrix() {
41     delete[]ptr;
42 }
43 
44 //按行賦值
45 void Matrix::set(const double* pvalue) {
46     std::memcpy(ptr, pvalue, sizeof(double) * lines * cols);
47 }
48 
49 //置零
50 void Matrix::clear() {
51     std::memset(ptr, 0, sizeof(double) * lines * cols);
52 }
53 
54 //返回(i,j)位置元素的const引用
55 const double& Matrix::at(int i, int j)const {
56     assert(i >= 0 && j >= 0 && i < lines && j < cols);
57     return ptr[i * cols + j];//這裡好容易錯!
58 }
59 
60 //返回(i,j)位置元素的引用
61 double& Matrix::at(int i, int j) {
62     assert(i >= 0 && j >= 0 && i < lines && j < cols);
63     return ptr[i * cols + j];
64 }
65 
66 //返回行數
67 int Matrix::get_lines()const {
68     return lines;
69 }
70 
71 //返回列數
72 int Matrix::get_cols()const {
73     return cols;
74 }
75 
76 //按行顯示矩陣的元素值
77 void Matrix::display()const {
78     for (auto i = 0; i < lines; ++i) {
79         for (auto j = 0; j < cols; ++j) {
80             cout << at(i, j) << " ";
81         }
82         cout << endl;
83     }
84 }
Matrix.hpp
實驗3 類和物件_基礎程式設計2
 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 const int N = 1000;
10 
11 
12 // 輸出矩陣物件索引為index所在行的所有元素
13 void output(const Matrix& m, int index) {
14     assert(index >= 0 && index < m.get_lines());
15     for (auto j = 0; j < m.get_cols(); ++j)
16         cout << m.at(index, j) << ", ";
17     cout << "\b\b \n";
18 }
19 
20 
21 void test1() {
22     double x[1000] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
23     int n, m;
24     cout << "Enter n and m: ";
25     cin >> n >> m;
26     Matrix m1(n, m); // 建立矩陣物件m1, 大小n×m
27     m1.set(x); // 用一維陣列x的值按行為矩陣m1賦值
28     Matrix m2(m, n); // 建立矩陣物件m1, 大小m×n
29     m2.set(x); // 用一維陣列x的值按行為矩陣m1賦值
30     Matrix m3(2); // 建立一個2×2矩陣物件
31     m3.set(x); // 用一維陣列x的值按行為矩陣m4賦值
32     cout << "矩陣物件m1: \n"; m1.display(); cout << endl;
33     cout << "矩陣物件m2: \n"; m2.display(); cout << endl;
34     cout << "矩陣物件m3: \n"; m3.display(); cout << endl;
35 }
36 
37 
38 void test2() {
39     Matrix m1(2, 3);
40     m1.clear();
41     const Matrix m2(m1);
42     m1.at(0, 0) = -999;
43     cout << "m1.at(0, 0) = " << m1.at(0, 0) << endl;
44     cout << "m2.at(0, 0) = " << m2.at(0, 0) << endl;
45     cout << "矩陣物件m1第0行: "; output(m1, 0);
46     cout << "矩陣物件m2第0行: "; output(m2, 0);
47 }
48 
49 
50 int main() {
51     cout << "測試1: \n";
52     test1();
53     cout << "測試2: \n";
54     test2();
55 }
task4.cpp

執行測試截圖

實驗任務五

原始碼

實驗3 類和物件_基礎程式設計2
 1 #pragma once
 2 
 3 #include<iostream>
 4 #include<string>
 5 
 6 using std::string;
 7 using std::endl;
 8 using std::cout;
 9 using std::cin;
10 
11 class User {
12 private:
13     string user;
14     string id;
15     string email;
16     string password;
17 
18 public:
19     //user必須輸入
20     User(const string &user,const string &id="",const string&email="")
21         :user(user), id(id), email(email), password("123456") {}
22     //User(const string& user)
23     //    :user(user), id(""), email(""), password("") {}
24 
25     void set_email() {
26         email.clear();
27         cout << "Enter email address: ";
28         std::getline(cin, email);//能讀取空格
29         while (email.find('@') == std::string::npos) {
30             email.clear();
31             cout << "illegal email.Please re-enter email:";
32             std::getline(cin, email);
33         }
34         cout << "email is set successfully..."<<endl;
35     }
36 
37     void change_password() {
38         //驗證密碼
39         cout << "Enter old password: ";
40         string old_pass;
41         std::getline(cin, old_pass);
42         int cnt = 0;//記錄輸入錯誤次數,三次鎖定
43         while (old_pass != password)
44         {
45             cnt++;
46             if (cnt == 3) {
47                 cout << "password input error. Please try after a while." << endl;
48                 return;
49             }
50             old_pass.clear();
51             cout << "password input error. Please re-enter again:";//老師你again打錯了:(
52             std::getline(cin,old_pass);
53         }
54         password.clear();
55         cout << "Enter new password: ";
56         std::getline(cin, password);
57         cout << "new password is set successfully..." << endl;
58     }
59 
60     void display()const {
61         cout << "name:  " << user << endl;
62         string password_private(password.size(), '*');
63         cout << "pass:  " << password_private << endl;
64         cout << "email:  " << email << endl;
65         cout << endl;
66     }
67 };
User.hpp
實驗3 類和物件_基礎程式設計2
 1 #include "user.hpp"
 2 #include <iostream>
 3 #include <vector>
 4 #include <string>
 5 using std::cin;
 6 using std::cout;
 7 using std::endl;
 8 using std::vector;
 9 using std::string;
10 
11 
12 void test() {
13     vector<User> user_lst;
14     User u1("Alice", "2024113", "Alice@hotmail.com");
15     user_lst.push_back(u1);
16     cout << endl;
17     User u2("Bob");
18     u2.set_email();
19     u2.change_password();
20     user_lst.push_back(u2);
21     cout << endl;
22     User u3("Hellen");
23     u3.set_email();
24     u3.change_password();
25     user_lst.push_back(u3);
26     cout << endl;
27     cout << "There are " << user_lst.size() << " users. they are: " << endl;
28     for (auto& i : user_lst) {
29         i.display();
30         cout << endl;
31     }
32 }
33 
34 int main() {
35     test();
36 }
task5.cpp

執行測試截圖

實驗任務六

原始碼

實驗3 類和物件_基礎程式設計2
 1 #pragma once 
 2 #include <iostream>  
 3 #include <cstdlib>  
 4 using namespace std;
 5 
 6 class Date {
 7 private:
 8     int year;
 9     int month;
10     int day;
11     int totalDays;
12 public:
13     Date(int year, int month, int day);
14     int getYear() const { return year; }
15     int getMonth() const { return month; }
16     int getDay() const { return day; }
17     int getMaxDay() const;
18     bool isLeapYear() const {
19         return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
20     }
21     void show() const;
22     int distance(const Date& date) const {
23         return totalDays - date.totalDays;
24     }
25 };
26 
27 namespace {
28     const int DAYS_BEFORE_MONTH[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
29 }
30 
31 Date::Date(int year, int month, int day) : year(year), month(month), day(day) {
32     if (day <= 0 || day > getMaxDay()) {
33         cout << "Invalid date: ";
34         show();
35         cout << endl;
36         exit(1);
37     }
38     int years = year - 1;
39     totalDays = years * 365 + years / 4 - years / 100 + years / 400 + DAYS_BEFORE_MONTH[month - 1] + day;
40     if (isLeapYear() && month > 2) totalDays++;
41 }
42 
43 int Date::getMaxDay() const {
44     if (isLeapYear() && month == 2)
45         return 29;
46     else
47         return DAYS_BEFORE_MONTH[month] - DAYS_BEFORE_MONTH[month - 1];
48 }
49 
50 void Date::show() const {
51     cout << getYear() << "-" << getMonth() << "-" << getDay();
52 }
date.hpp
實驗3 類和物件_基礎程式設計2
 1 #pragma once  
 2 #include "date.hpp"  
 3 #include <string>  
 4 #include <cmath>  
 5 #include <iostream>  
 6 using namespace std;
 7 
 8 class SavingsAccount {
 9 private:
10     string id;
11     double balance;
12     double rate;
13     Date lastDate;
14     double accumulation;
15     static double total;
16     void record(const Date& date, double amount, const string& desc);
17     void error(const string& msg) const;
18     double accumulate(const Date& date) const {
19         return accumulation + balance * date.distance(lastDate);
20     }
21 public:
22     SavingsAccount(const Date& date, const string& id, double rate);
23     const string& getId() const { return id; }
24     double getBalance() const { return balance; }
25     double getRate() const { return rate; }
26     static double getTotal() { return total; }
27     void deposit(const Date& date, double amount, const string& desc);
28     void withdraw(const Date& date, double amount, const string& desc);
29     void settle(const Date& date);
30     void show() const;
31 };
32 
33 double SavingsAccount::total = 0;
34 
35 SavingsAccount::SavingsAccount(const Date& date, const string& id, double rate) :
36     id(id), balance(0), rate(rate), lastDate(date), accumulation(0) {
37     date.show();
38     cout << "\t#" << id << " created" << endl;
39 }
40 
41 void SavingsAccount::record(const Date& date, double amount, const string& desc) {
42     accumulation = accumulate(date);
43     lastDate = date;
44     amount = floor(amount * 100 + 0.5) / 100;
45     balance += amount;
46     total += amount;
47     date.show();
48     cout << "\t#" << id << "\t" << amount << "\t" << balance << "\t" << desc << endl;
49 }
50 
51 void SavingsAccount::error(const string& msg) const {
52     cout << "Error(#" << id << "):" << msg << endl;
53 }
54 
55 void SavingsAccount::deposit(const Date& date, double amount, const string& desc) {
56     record(date, amount, desc);
57 }
58 
59 void SavingsAccount::withdraw(const Date& date, double amount, const string& desc) {
60     if (amount > getBalance())
61         error("not enough money");
62     else
63         record(date, -amount, desc);
64 }
65 
66 void SavingsAccount::settle(const Date& date) {
67     double interest = accumulate(date) * rate / date.distance(Date(date.getYear() - 1, 1, 1));
68     if (interest != 0) record(date, interest, "interest");
69     accumulation = 0;
70 }
71 
72 void SavingsAccount::show() const {
73     cout << id << "\tBalance: " << balance;
74 }
account.cpp
實驗3 類和物件_基礎程式設計2
 1 #include "account.hpp"  
 2 #include <iostream>  
 3 using namespace std;
 4 
 5 int main() {
 6     Date date{ 2008, 11, 1 };
 7     SavingsAccount accounts[] = {
 8         SavingsAccount(date, "03755217", 0.015),
 9         SavingsAccount(date, "02342342", 0.015)
10     };
11     const int n = sizeof(accounts) / sizeof(SavingsAccount);
12     accounts[0].deposit(Date(2008, 11, 5), 5000, "salary");
13     accounts[1].deposit(Date(2008, 11, 25), 10000, "sell stock 0323");
14     accounts[0].deposit(Date(2008, 12, 5), 5500, "salary");
15     accounts[1].withdraw(Date(2008, 12, 20), 4000, "buy a laptop");
16     cout << endl;
17     for (int i = 0; i < n; i++) {
18         accounts[i].settle(Date(2009, 1, 1));
19         accounts[i].show();
20         cout << endl;
21     }
22     cout << "Total: " << SavingsAccount::getTotal() << endl;
23     return 0;
24 }
task6.cpp

執行測試截圖

相關文章