實驗3 c++

观者如云發表於2024-11-10

任務一:

button.hpp:

#pragma once
#include "button.hpp"
#include <vector>
#include <iostream>
using std::vector;
using std::cout;
using std::endl;
// 視窗類
class Window {
public:
	Window(const string& win_title);
	void display() const;
	void close();
	void add_button(const string& label);
private:
	string title;
	vector<Button> buttons;
};
Window::Window(const string& win_title) : title{ win_title } {
	buttons.push_back(Button("close"));
}
inline void Window::display() const {
	string s(40, '*');
	cout << s << endl;
	cout << "window title: " << title << endl;
	cout << "It has " << buttons.size() << " buttons: " << endl;
	for (const auto& i : buttons)
		cout << i.get_label() << " button" << endl;
	cout << s << endl;
}
void Window::close() {
	cout << "close window '" << title << "'" << endl;
	buttons.at(0).click();
}
void Window::add_button(const string& label) {
	buttons.push_back(Button(label));
}

  

window.hpp:

#pragma once
#include "button.hpp"
#include <vector>
#include <iostream>
using std::vector;
using std::cout;
using std::endl;
// 視窗類
class Window {
public:
	Window(const string& win_title);
	void display() const;
	void close();
	void add_button(const string& label);
private:
	string title;
	vector<Button> buttons;
};
Window::Window(const string& win_title) : title{ win_title } {
	buttons.push_back(Button("close"));
}
inline void Window::display() const {
	string s(40, '*');
	cout << s << endl;
	cout << "window title: " << title << endl;
	cout << "It has " << buttons.size() << " buttons: " << endl;
	for (const auto& i : buttons)
		cout << i.get_label() << " button" << endl;
	cout << s << endl;
}
void Window::close() {
	cout << "close window '" << title << "'" << endl;
	buttons.at(0).click();
}
void Window::add_button(const string& label) {
	buttons.push_back(Button(label));
}

  

task1.cpp:

#include "window.hpp"
#include <iostream>
using std::cout;
using std::cin;
void test() {
	Window w1("new window");
	w1.add_button("maximize");
	w1.display();
	w1.close();
}
int main() {
	cout << "用組合類模擬簡單GUI:\n";
	test();
}

  

實驗結果截圖:

問題一:自定義了兩個類使用了標準庫的iostream,string,vector類,button類和iostream,string組合,window類和button,iostream,vector類組合

問題二:如果成員函式只是訪問資料而不改變資料,就可以加上const來增加程式碼的安全性。如果函式簡單並且呼叫頻繁,則可以加上inline來提高效能

問題三:程式碼的功能是生成一條分割線,可用於裝飾和視覺輔助。

任務二:

task2.cpp:

#include <iostream>
#include <vector>
using namespace std;
void output1(const vector<int>& v) {
	for (auto& i : v)
		cout << i << ", ";
	cout << "\b\b \n";
}
void output2(const vector<vector<int>> v) {
	for (auto& i : v) {
		for (auto& j : i)
			cout << j << ", ";
		cout << "\b\b \n";
	}
}
void test1() {
	vector<int> v1(5, 42);
	const vector<int> v2(v1);
	v1.at(0) = -999;
	cout << "v1: ";output1(v1);
	cout << "v2: ";output1(v2);
	cout << "v1.at(0) = " << v1.at(0) << endl;
	cout << "v2.at(0) = " << v2.at(0) << endl;
}
void test2() {
	vector<vector<int>> v1{ {1, 2, 3}, {4, 5, 6, 7} };
	const vector<vector<int>> v2(v1);
	v1.at(0).push_back(-999);
	cout << "v1: \n";output2(v1);
	cout << "v2: \n";output2(v2);
	vector<int> t1 = v1.at(0);
	cout << t1.at(t1.size() - 1) << endl;

	const vector<int> t2 = v2.at(0);
	cout << t2.at(t2.size() - 1) << endl;
}
int main() {
	cout << "測試1:\n";
	test1();
	cout << "\n測試2:\n";
	test2();
}

實驗結果截圖:

問題一:建立一個整數向量v1,大小為5,所有元素初始化為42,建立向量v2,複製v1內容,並把v1的第一個元素改為-999。

問題二:建立一個二維向量v1,第一行為1,2,3、第二行為4、5、6、7,後建立一個v1的副本v2,最後在v1第一行的結尾插入-999

問題三:建立一個一維向量t1,複製v1第一行的內容,並輸出t1的最後一個元素,同理t2。

問題四:vector內部封裝的複製建構函式是深複製,為每個元素提供獨立的副本。需要提供一個const成員函式作為介面,供const vector使用。

任務三:

vectorInt.hpp:

#pragma once

#include <iostream>
#include <cassert>

using std::cout;
using std::endl;

// 動態int陣列物件類
class vectorInt {
public:
    vectorInt(int n);
    vectorInt(int n, int value);
    vectorInt(const vectorInt& vi);
    ~vectorInt();

    int& at(int index);
    const int& at(int index) const;

    vectorInt& assign(const vectorInt& v);
    int get_size() const;

private:
    int size;
    int* ptr;       // ptr指向包含size個int的陣列
};

vectorInt::vectorInt(int n) : size{ n }, ptr{ new int[size] } {
}

vectorInt::vectorInt(int n, int value) : size{ n }, ptr{ new int[size] } {
    for (auto i = 0; i < size; ++i)
        ptr[i] = value;
}

vectorInt::vectorInt(const vectorInt& vi) : size{ vi.size }, ptr{ new int[size] } {
    for (auto i = 0; i < size; ++i)
        ptr[i] = vi.ptr[i];
}

vectorInt::~vectorInt() {
    delete[] ptr;
}

const int& vectorInt::at(int index) const {
    assert(index >= 0 && index < size);

    return ptr[index];
}

int& vectorInt::at(int index) {
    assert(index >= 0 && index < size);

    return ptr[index];
}

vectorInt& vectorInt::assign(const vectorInt& v) {
    delete[] ptr;       // 釋放物件中ptr原來指向的資源

    size = v.size;
    ptr = new int[size];

    for (int i = 0; i < size; ++i)
        ptr[i] = v.ptr[i];

    return *this;
}

int vectorInt::get_size() const {
    return size;
}

task3.cpp:

#include "vectorInt.hpp"
#include <iostream>

using std::cin;
using std::cout;

void output(const vectorInt& vi) {
    for (auto i = 0; i < vi.get_size(); ++i)
        cout << vi.at(i) << ", ";
    cout << "\b\b \n";
}


void test1() {
    int n;
    cout << "Enter n: ";
    cin >> n;

    vectorInt x1(n);
    for (auto i = 0; i < n; ++i)
        x1.at(i) = i * i;
    cout << "x1: ";  output(x1);

    vectorInt x2(n, 42);
    vectorInt x3(x2);
    x2.at(0) = -999;
    cout << "x2: ";  output(x2);
    cout << "x3: ";  output(x3);
}

void test2() {
    const vectorInt  x(5, 42);
    vectorInt y(10, 0);

    cout << "y: ";  output(y);
    y.assign(x);
    cout << "y: ";  output(y);

    cout << "x.at(0) = " << x.at(0) << endl;
    cout << "y.at(0) = " << y.at(0) << endl;
}

int main() {
    cout << "測試1: \n";
    test1();

    cout << "\n測試2: \n";
    test2();
}

實驗結果截圖:

問題一:是深複製,為新的程式碼物件分配了新的記憶體空間。

問題二:如果返回值改為int無法正確執行測試程式碼,因為無法透過at修改元素了,有潛在安全隱患,外部程式碼可能會修改內部資料。

問題三:不可以,如果返回值型別是 vectorint 而不是引用,則每次呼叫時,都會建立一個vectorint物件的副本。這意味著在函式返回之前,會發生一個完整的物件複製,對效能有顯著影響。

任務四:

matrix.hpp:

#include <iostream>
#include <cassert>
#include <iomanip> // 用於 std::setprecision

using std::cout;
using std::endl;

// 類Matrix的實現
class Matrix {
public:
    Matrix(int n, int m);           // 建構函式,構造一個n*m的矩陣, 初始值為0
    Matrix(int n);                  // 建構函式,構造一個n*n的矩陣, 初始值為0
    Matrix(const Matrix& x);        // 複製建構函式,使用已有的矩陣X構造
    ~Matrix();                      // 解構函式

    void set(const double* pvalue); // 用pvalue指向的連續記憶體塊資料按行為矩陣賦值
    void clear();                   // 把矩陣物件的值置0

    const double& at(int i, int j) const; // 返回矩陣物件索引(i,j)的元素const引用
    double& at(int i, int j);               // 返回矩陣物件索引(i,j)的元素引用

    int get_lines() const;              // 返回矩陣物件行數
    int get_cols() const;               // 返回矩陣物件列數

    void display() const;                // 按行顯示矩陣物件元素值

private:
    int lines;      // 矩陣物件內元素行數
    int cols;       // 矩陣物件內元素列數
    double* ptr;    // 指向矩陣資料的指標
};

// 建構函式(n x m 矩陣)
Matrix::Matrix(int n, int m) : lines(n), cols(m) {
    ptr = new double[lines * cols](); // 初始化為0
}

// 建構函式(n x n 矩陣)
Matrix::Matrix(int n) : Matrix(n, n) {}

// 複製建構函式
Matrix::Matrix(const Matrix& x) : lines(x.lines), cols(x.cols) {
    ptr = new double[lines * cols];
    for (int i = 0; i < lines * cols; ++i) {
        ptr[i] = x.ptr[i]; // 深複製資料
    }
}

// 解構函式
Matrix::~Matrix() {
    delete[] ptr; // 釋放分配的記憶體
}

// 設定矩陣資料
void Matrix::set(const double* pvalue) {
    for (int i = 0; i < lines * cols; ++i) {
        ptr[i] = pvalue[i];
    }
}

// 清空矩陣
void Matrix::clear() {
    for (int i = 0; i < lines * cols; ++i) {
        ptr[i] = 0.0;
    }
}

// 獲取元素的 const 引用
const double& Matrix::at(int i, int j) const {
    assert(i >= 0 && i < lines && j >= 0 && j < cols); // 邊界檢查
    return ptr[i * cols + j]; // 計算並返回元素
}

// 獲取元素的引用
double& Matrix::at(int i, int j) {
    assert(i >= 0 && i < lines && j >= 0 && j < cols); // 邊界檢查
    return ptr[i * cols + j]; // 計算並返回元素
}

// 返回行數
int Matrix::get_lines() const {
    return lines;
}

// 返回列數
int Matrix::get_cols() const {
    return cols;
}

// 顯示矩陣
void Matrix::display() const {
    for (int i = 0; i < lines; ++i) {
        for (int j = 0; j < cols; ++j) {
            cout << std::setw(1) << at(i, j) << " "; // 格式化輸出
        }
        cout << endl;
    }
}

  

task4.cpp:

#include "matrix.hpp"
#include <iostream>
#include <cassert>

using std::cin;
using std::cout;
using std::endl;


const int N = 1000;

// 輸出矩陣物件索引為index所在行的所有元素
void output(const Matrix& m, int index) {
    assert(index >= 0 && index < m.get_lines());

    for (auto j = 0; j < m.get_cols(); ++j)
        cout << m.at(index, j) << ", ";
    cout << "\b\b \n";
}


void test1() {
    double x[1000] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    int n, m;
    cout << "Enter n and m: ";
    cin >> n >> m;

    Matrix m1(n, m);    // 建立矩陣物件m1, 大小n×m
    m1.set(x);          // 用一維陣列x的值按行為矩陣m1賦值

    Matrix m2(m, n);    // 建立矩陣物件m1, 大小m×n
    m2.set(x);          // 用一維陣列x的值按行為矩陣m1賦值

    Matrix m3(2);       // 建立一個2×2矩陣物件
    m3.set(x);          // 用一維陣列x的值按行為矩陣m4賦值

    cout << "矩陣物件m1: \n";   m1.display();  cout << endl;
    cout << "矩陣物件m2: \n";   m2.display();  cout << endl;
    cout << "矩陣物件m3: \n";   m3.display();  cout << endl;
}

void test2() {
    Matrix m1(2, 3);
    m1.clear();

    const Matrix m2(m1);
    m1.at(0, 0) = -999;

    cout << "m1.at(0, 0) = " << m1.at(0, 0) << endl;
    cout << "m2.at(0, 0) = " << m2.at(0, 0) << endl;
    cout << "矩陣物件m1第0行: "; output(m1, 0);
    cout << "矩陣物件m2第0行: "; output(m2, 0);
}

int main() {
    cout << "測試1: \n";
    test1();

    cout << "測試2: \n";
    test2();
}

  

實驗結果截圖:

任務五:

user.hpp:

#pragma once

#include <iostream>
#include <string>
#include <regex> // 用於正規表示式

using std::string;

class User {
private:
    string name;
    string password;
    string email;

public:
    // 建構函式,預設密碼和郵箱
    User(const string& userName, const string& pass = "123456", const string& userEmail = "")
        : name(userName), password(pass), email(userEmail) {
    }

    // 設定郵箱方法
    void set_email() {
        string inputEmail;
        std::regex emailRegex(R"(^[^\s@]+@[^\s@]+\.[^\s@]+$)"); // 簡單的郵箱格式檢查
        do {
            std::cout << "Enter your email: ";
            std::getline(std::cin, inputEmail);
            if (!std::regex_match(inputEmail, emailRegex)) {
                std::cout << "Invalid email format. Please try again." << std::endl;
            }
        } while (!std::regex_match(inputEmail, emailRegex));

        email = inputEmail; // 賦值
    }

    // 修改密碼方法
    void change_password() {
        string oldPassword;
        const int maxAttempts = 3;
        int attempts = 0;

        while (attempts < maxAttempts) {
            std::cout << "Enter your old password: ";
            std::getline(std::cin, oldPassword);
            if (oldPassword == password) {
                string newPassword;
                std::cout << "Enter your new password: ";
                std::getline(std::cin, newPassword);
                password = newPassword; // 更新密碼
                std::cout << "Password changed successfully." << std::endl;
                return;
            }
            else {
                std::cout << "Incorrect password. Try again." << std::endl;
                attempts++;
            }
        }
        std::cout << "Too many failed attempts. Please try again later." << std::endl;
    }

    // 顯示使用者資訊
    void display() const {
        std::cout << "User Name: " << name << "\n"
            << "Password: " << std::string(password.length(), '*') << "\n"
            << "Email: " << email << std::endl;
    }
};

  

task5.cpp:

#include "user.hpp"
#include <iostream>
#include <vector>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::vector;
using std::string;

void test() {
    vector<User> user_lst;

    User u1("Alice", "2024113", "Alice@hotmail.com");
    user_lst.push_back(u1);
    cout << endl;

    User u2("Bob");
    u2.set_email();
    u2.change_password();
    user_lst.push_back(u2);
    cout << endl;

    User u3("Hellen");
    u3.set_email();
    u3.change_password();
    user_lst.push_back(u3);
    cout << endl;

    cout << "There are " << user_lst.size() << " users. they are: " << endl;
    for (auto& i : user_lst) {
        i.display();
        cout << endl;
    }
}

int main() {
    test();
}

  

實驗結果截圖:

任務六:

date.h:

#ifndef __DATE_H__
#define __DATE_H__
class Date {
private:
    int year;
    int month;
    int day;
    int totalDays;

public:
    Date(int year, int month, int day);
    int getYear()const{ return year;}
    int getMonth()const{ return month;}
    int getDay()const{ return day;}
    int getMaxDay()const;
    bool isLeapYear()const{
        return year%4==0&&year%100!=0||year%400==0;
    }
    void show()const;
    int distance(const Date& date)const {
        return totalDays - date.totalDays;
    }
};
#endif // __DATE_H__

  

date.cpp:

#include"date.h"
#include<iostream>
#include<cstdlib>
using namespace std;
namespace {
	const int DAYS_BEFORE_MONTH[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
}
Date::Date(int year, int month, int day) :year(year),month(month),day(day) {
	if(day<=0||day>getMaxDay()){
		cout << "Invalid date!";
		show();
		cout << endl;
		exit(1);
	}
int years=year-1;
totalDays=years*365+years/4-years/100+years/400+DAYS_BEFORE_MONTH[month-1]+day;
if(isLeapYear()&&month>2)
	totalDays++;
}
int Date::getMaxDay() const {
	if(isLeapYear()&&month==2)
		return 29;
	else
		return DAYS_BEFORE_MONTH[month]-DAYS_BEFORE_MONTH[month-1];
}
void Date::show() const {
	cout << getYear()<<"-"<<getMonth()<<"-"<<getDay();
}

  

account.h:

#ifndef ACCOUNT_H
#define ACCOUNT_H

#include "date.h"
#include <string>

class SavingsAccount {
private:
    // 儲蓄賬戶類
    std::string id;
    double balance;
    // 賬號
    double rate;
    // 餘額
    Date lastDate;
    // 存款的年利率
    double accumulation;
    // 上次變更餘額的時期
    static double total;
    // 餘額按日累加之和
    // 所有賬戶的總金額

    // 記錄一筆賬, date為日期, amount為金額, desc為說明
    void record(const Date& date, double amount, const std::string& desc);
    // 報告錯誤資訊
    void error(const std::string& msg) const;

    // 獲得到指定日期為止的存款金額按日累積值
    double accumulate(const Date& date) const {
        return accumulation + balance * date.distance(lastDate);
    }

public:
    // 建構函式
    SavingsAccount(const Date& date, const std::string& id, double rate);

    const std::string& getId() const { return id; }
    double getBalance() const { return balance; }
    double getRate() const { return rate; }
    static double getTotal() { return total; }

    // 存入現金
    void deposit(const Date& date, double amount, const std::string& desc);
    // 取出現金
    void withdraw(const Date& date, double amount, const std::string& desc);
    // 結算利息, 每年1月1日呼叫一次該函式
    void settle(const Date& date);
    // 列印賬戶資訊
    void show() const;
};

#endif // ACCOUNT_H

  

account.cpp:

// account.cpp
#include "account.h"
#include <cmath>
#include <iostream>
using namespace std;

double SavingsAccount::total = 0;

// SavingsAccount類相關成員函式的實現
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;
    }

  

6_25.cpp:

// 6_25.cpp
#include "account.h"
#include <iostream>
using namespace std;

int main() {
    Date date(2008, 11, 1);

    SavingsAccount accounts[] = {
        SavingsAccount(date, "03755217", 0.015),
        SavingsAccount(date, "02342342", 0.015)
    };

    const int n = sizeof(accounts) / sizeof(SavingsAccount);

    accounts[0].deposit(Date(2008, 11, 5), 5000, "salary");
    accounts[1].deposit(Date(2008, 11, 25), 10000, "sell stock 0323");

    accounts[0].deposit(Date(2008, 12, 5), 5500, "salary");
    accounts[1].withdraw(Date(2008, 12, 20), 4000, "buy a laptop");

    cout << endl;

    for (int i = 0; i < n; i++) {
        accounts[i].settle(Date(2009, 1, 1));
        accounts[i].show();
        cout << endl;
    }

    cout << "Total: " << SavingsAccount::getTotal() << endl;

    return 0;
}

  

實驗結果截圖:

相關文章