实验3 类和对象——基础编程2

一、实验目的

 加深对类的组合机制的理解,会正确使用C++正确定义,使用组合类

理解深复制,浅复制

练习标准库string ,vector的用法,能基于问题场景灵活使用

针对具体问题场景,练习运用面向对象思维进行设计,合理设计,组合类(自定义/标准库),编程解决实际问题。

二、实验准备

 系统复习浏览以下内容

类的抽象,设计

组合类:要解决的问题场景,定义和使用方法

数据共享,保护

标准库string ,vector的用法

 

三、实验内容

 

1. 实验任务1

代码:button.hpp

 1 #pragma once 
 2 #include <iostream>
 3 #include<string>
 4 
 5 using std::string;
 6 using std::cout;
 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 Button::Button(const string &text):label {text}{
17 
18 } 
19 
20 inline string Button::get_label()const {
21 return label;}
22 
23 void Button::click(){
24     
25     cout<<"Button "<<label<<"clicked\n";
26     
27 }

window.hpp

 1 #pragma once 
 2 #include"button.hpp"
 3 #include<vector>
 4 #include<iostream>
 5 
 6 
 7 using std::vector;
 8 using std::cout;
 9 using std::endl;
10 
11 class Window{
12     public:
13         Window(const string &win_title);
14         void display()const;
15         void close();
16         void add_button(const string &label);
17     private:
18         string title;
19         vector <Button> buttons;
20         
21 };
22 Window::Window(const string &win_title):title{win_title}{
23 buttons.push_back(Button("close "));}
24 
25 
26 inline void Window::display()const{
27 string s(40,'*');
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 cout<<s<<endl;
34 
35 
36 }
37 void Window::close(){
38     cout<<"close window"<<" '"<<title<<"'"<<endl;
39     buttons.at(0).click();
40     
41 }
42 
43 void Window::add_button(const string &label){
44     buttons.push_back(Button(label));
45     
46 }

task1.cpp

 1 #include"window.hpp"
 2 #include<iostream>
 3 
 4 
 5 using std::cout;
 6 using std::cin;
 7 
 8 void test(){
 9     
10     Window w1("new window");
11     w1.add_button("maximize ");
12     w1.display();
13     w1.close();
14 }
15 
16 int main(){
17     cout<<"用组合类模拟简单GUI:\n";
18     test();
19 }

 

 

 

运行截图:

 

 

 

问题回答:

问题一:这个示例代码中,自定义了2个类,一个Button,一个Window类,使用到了标准库的string和vector类。其中string和Button类,string 和Window类,vector和Window类存在组合关系

问题二:在Button类当中,  get_label()函数被标记为const,这是合适的,因为它只返回标签而不修改任何数据。或者设置成inline,因为它简单且频繁调用。在Windows类当中,disliay()函数可以被标记为const,因为它只显示窗口信息而不改变。但不应设置成inline,因为它的代码量太大了。close()不应设置成为const因为它会改变

问题三:string s(40,'*');是创建一个名为s 的字符串,长度为40,且所有字符均为*

 

 

2. 实验任务2

代码:

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

 

 

运行截图

 

 

 

 

问题回答:

问题一:

vector<int> v1(5,42);
const vector<int> v2(v1);

v1.at(0)=-999;

 这个代码是定义一个常量引用的整型向量v1,内部包含5个元素,且全部初始化值为42,把v1的值拷贝给了v2,v2成为了不可变的一维向量;而后又将第一个元素改成了-999;

问题二:

vector<vector<int>> v1{{1,2,3},{4,5,6,7}};
const vector <vector<int>> v2(v1);

v1.at(0).push_back(-999);

  这个代码是定义一个常量的二维整型向量v1,{{1,2,3},{4,5,6,7}};表示v1包含两个内层向量

第一个内层向量是{1,2,3},包含三个元素。第二个内层向量是{4,5,6,7},包含四个元素。同样把v1的值拷贝给了v2,v2成为了不可变的二维向量;

而后又把-999增加到了v1的第一层的末尾,变成了v1{{1,2,3,-999},{4,5,6,7}};

3. 实验任务3

代码:

 vectorInt.hpp

 1 #pragma once
 2 
 3 #include <iostream>
 4 #include <cassert>
 5 
 6 using std::cout;
 7 using std::endl;
 8 
 9 
10 class vectorInt{
11 public:
12     vectorInt(int n);
13     vectorInt(int n, int value);
14     vectorInt(const vectorInt &vi);
15     ~vectorInt();
16 
17     int& at(int index);
18     const int& at(int index) const;
19 
20     vectorInt& assign(const vectorInt &v);
21     int get_size() const;
22 
23 private:
24     int size;
25     int *ptr;      
26 };
27 
28 vectorInt::vectorInt(int n): size{n}, ptr{new int[size]} {
29 }
30 
31 vectorInt::vectorInt(int n, int value): size{n}, ptr{new int[size]} {
32     for(auto i = 0; i < size; ++i)
33         ptr[i] = value;
34 }
35 
36 vectorInt::vectorInt(const vectorInt &vi): size{vi.size}, ptr{new int[size]} {
37     for(auto i = 0; i < size; ++i)
38         ptr[i] = vi.ptr[i];
39 }
40 
41 vectorInt::~vectorInt() {
42     delete [] ptr;
43 }
44 
45 const int& vectorInt::at(int index) const {
46     assert(index >= 0 && index < size);
47 
48     return ptr[index];
49 }
50 
51 int& vectorInt::at(int index) {
52     assert(index >= 0 && index < size);
53 
54     return ptr[index];
55 }
56 
57 vectorInt& vectorInt::assign(const vectorInt &v) {  
58     delete[] ptr;     
59 
60     size = v.size;
61     ptr = new int[size];
62 
63     for(int i = 0; i < size; ++i)
64         ptr[i] = v.ptr[i];
65 
66     return *this;
67 }
68 
69 int vectorInt::get_size() const {
70     return size;
71 }

task3.cpp

 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\b \n";
11 }
12 
13 
14 void test1() {
15     int n;
16     cout << "Enter n: ";
17     cin >> n;
18 
19     vectorInt x1(n);
20     for(auto i = 0; i < n; ++i)
21         x1.at(i) = i*i;
22     cout << "x1: ";  output(x1);
23 
24     vectorInt x2(n, 42);
25     vectorInt x3(x2);
26     x2.at(0) = -999;
27     cout << "x2: ";  output(x2);
28     cout << "x3: ";  output(x3);
29 }
30 
31 void test2() {
32     const vectorInt  x(5, 42);
33     vectorInt y(10, 0);
34 
35     cout << "y: ";  output(y);
36     y.assign(x);
37     cout << "y: ";  output(y);
38     
39     cout << "x.at(0) = " << x.at(0) << endl;
40     cout << "y.at(0) = " << y.at(0) << endl;
41 }
42 
43 int main() {
44     cout << "测试1: \n";
45     test1();
46 
47     cout << "\n测试2: \n";
48     test2();
49 }

 

 

运行截图:

 

 

 

 问题回答:

 问题一:  vectorInt(const vectorInt &vi);的实现是深复制。复制一个vectorInt对象,会分配新的内存并复制原对象中的元素,包括指针。

问题二:vectorInt类中,这两个at()接口,如果返回值类型改成int而非int&(相应地,实现部分也 同步修改),测试代码还能正确运行吗?能正常运行,但也意味着返回的是元素的值而不是对该值的引用。在这种情况下,调用 at() 的代码将能够正常运行,但会失去对原始数组元素的修改能力

如果把line18返回值类型前面的const掉,针对这个测试 代码,是否有潜在安全隐患?会存在安全隐患。返回一个非 const 引用可能会允许外部代码修改对象的内部数据结构。

问题三:assign() 方法的返回值类型可以改成vectorInt,但是每次调用之后返回的不是原对象的引用,而是一个副本。

 

 

4. 实验任务4

代码:

 

 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();                           // 把矩阵对象的值置0
20     
21     const double& at(int i, int j) const;   // 返回矩阵对象索引(i,j)的元素const引用
22     double& at(int i, int j);               // 返回矩阵对象索引(i,j)的元素引用
23     
24     int get_lines() const;                  // 返回矩阵对象行数
25     int get_cols() const;                   // 返回矩阵对象列数
26 
27     void display() const;                    // 按行显示矩阵对象元素值
28 
29 private:
30     int lines;      // 矩阵对象内元素行数
31     int cols;       // 矩阵对象内元素列数
32     double *ptr;
33 };
34 
35 Matrix::Matrix(int n, int m):lines{n},cols{m},ptr{new double[n*m]}{
36  
37 }           // 构造函数,构造一个n*m的矩阵, 初始值为value
38    Matrix::Matrix(int n) :lines{n},cols{n}, ptr{new double[n*n] }{}
39                  // 构造函数,构造一个n*n的矩阵, 初始值为value
40   Matrix::Matrix(const Matrix &x):lines{x.lines},cols(x.cols),ptr(new double[x.lines*x.cols]){
41   std::memcpy(ptr,x.ptr,lines*cols*sizeof(double));
42               }        // 复制构造函数, 使用已有的矩阵X构造
43     Matrix::~Matrix(){delete [] ptr;
44     }
45 
46     void Matrix::set(const double *pvalue){
47     for (auto i = 0; i < lines; ++i) {
48         for (auto j = 0; j < cols; ++j) {
49             ptr[i * cols + j] = pvalue[i * cols + j];
50         }
51     }}
52         // 用pvalue指向的连续内存块数据按行为矩阵赋值
53     void Matrix::clear(){std::memset(ptr ,0,lines*cols*sizeof(double));}                           // 把矩阵对象的值置0
54     
55     const double& Matrix::at(int i, int j) const {
56     assert(i >= 0 && i < lines && j >= 0 && j < cols);
57     return ptr[i * cols + j];
58 }   // 返回矩阵对象索引(i,j)的元素const引用
59     double& Matrix::at(int i, int j){
60     assert(i >= 0 && i < lines && j >= 0 && j < cols);
61     return ptr[i * cols + j];
62 }               // 返回矩阵对象索引(i,j)的元素引用
63     
64     int Matrix::get_lines() const{
65     return lines;}                  // 返回矩阵对象行数
66     int Matrix::get_cols() const{
67     return cols;}                  // 返回矩阵对象列数
68 
69     void Matrix::display() const{
70             for (auto i =0;i<lines;++i){
71            for(auto j =0;j<cols ;++j){
72                    cout<<at(i,j);
73                     if (j < cols - 1) {
74                 cout << ",";  }
75                    cout<<endl;}                 // 按行显示矩阵对象元素值
76 }

 

 

运行截图:

 

 

 

 

 

 

 

5. 实验任务5

代码:

 

 1 #ifndef USER_HPP
 2 #define USER_HPP
 3 
 4 #include <iostream>
 5 #include <string>
 6 #include <limits>
 7 
 8 class User {
 9 private:
10     std::string name;     // 用户名
11     std::string password; // 密码
12     std::string email;    // 邮箱
13 
14 public:
15     // 构造函数
16     User(const std::string& name0, const std::string& password0 = "123456", const std::string& mail0 = "")
17         : name(name0), password(password0), email(mail0) {}
18 
19     // 接口1,用来提示设置邮箱
20     void set_email() {
21     std::string test_email;
22     
23     while (true) { 
24         std::cout << "Enter email address: ";
25         std::cin >> test_email;
26 
27         if (test_email.find('@') == std::string::npos) {
28             std::cout << "Illegal email. Please re-enter email: ";
29         } else {
30             email = test_email;
31             std::cout << "Email is set successfully..." << std::endl;
32             break; 
33         }
34     }
35 }
36 
37     // 接口2,用来修改密码
38     void change_password() {
39         std::string old_password, new_password; // 定义 new_password
40         int attempts = 0;
41         const int max_attempts = 3;
42 
43         while (attempts < max_attempts) {
44             std::cout << "Enter old password: ";
45             std::cin >> old_password;
46            
47             if (old_password == password) {
48                 std::cout << "Enter new password: ";
49                 std::cin >> new_password;
50                 password = new_password;
51                 std::cout << "new password is set successfully..." << std::endl;
52                 return;
53             } else {
54                 attempts++;
55               
56                 if (attempts == max_attempts) {
57                     std::cout << "password input error. Please try after a while." << std::endl;
58                 } else {
59                     std::cout << "password input error. Please re-enter again." << std::endl;
60                 }
61                 
62             }
63         }
64     }
65 
66     // 接口3,用来打印用户信息
67     void display() const {
68         std::cout << "name: " << name << "\n";
69         std::cout << "pass: ";
70         for (int i = 0; i < password.length(); ++i) {
71             std::cout << "*"; 
72         }
73         std::cout << std::endl;
74         std::cout << "email: " << email << std::endl;
75     }
76 };
77 
78 #endif 

 

 

运行截图:

 

 

 

 

 

 

 

6. 实验任务6

代码:

 data.h

 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  };

data.cpp

 1 #include"data.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 }

account.h

 1 #pragma once
 2 #include"data.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 
26     const std::string& getId() const { return id; }
27     double getBalance() const { return balance; }
28 
29     double getRate() const { return rate; }
30 
31     static double getTotal() { return total; }
32 
33     void deposit(const Date& date, double amount, const std::string& desc);
34 
35     void withdraw(const Date& date, double amount, const std::string& desc);
36 
37     void settle(const Date& date);
38 
39     void show() const;
40  };

account.cpp

 1 #include "account.h"
 2 #include<cmath>
 3 #include<iostream>
 4  
 5 using namespace std;
 6 
 7 double SavingsAccount::total = 0;
 8 
 9 SavingsAccount::SavingsAccount(const Date& date, const string& id, double rate) : id(id), balance(0), rate(rate), lastDate(date), accumulation(0) 
10 {
11     date.show();
12     cout << "\t#" << id << "  created" << endl;
13  }
14 
15 void SavingsAccount::record(const Date& date, double amount, const string& desc) 
16 {
17     accumulation = accumulate(date);
18  
19     lastDate = date;
20 
21     amount = floor(amount * 100 + 0.5) / 100; //保留小数点后两位
22     balance += amount;
23     total += amount;
24     date.show();
25  
26      cout << "\t#" << id << "\t" << amount << "\t" << balance << "\t" << desc << endl;
27  
28  }
29 void SavingsAccount::error(const string& msg)const 
30 {
31     cout << "Error(#" << id << "):" << msg << endl;
32 
33 }
34 
35  void SavingsAccount::deposit(const Date& date, double amount, const string& desc) 
36 {
37     record(date, amount, desc);
38  
39 }
40 
41  void SavingsAccount::withdraw(const Date& date, double amount, const string& desc) 
42  {
43     if (amount > getBalance())
44         error("not enough money");
45     else
46         record(date, -amount, desc);
47 
48  }
49  
50  void SavingsAccount::settle(const Date& date) 
51 {
52 
53     double interest = accumulate(date) * rate / date.distance(Date(date.getYear() - 1, 1, 1)); //计算年息
54 
55     if (interest != 0)
56  
57          record(date, interest, "interest");
58 
59      accumulation = 0;
60  
61 }
62 
63 void SavingsAccount::show() const 
64 {
65     cout << id << "\tBalance:" << balance;
66  }

6_25.cpp

 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  }

 

 

运行截图:

 

 

posted @ 2024-11-09 20:10  安东尼23  阅读(11)  评论(0编辑  收藏  举报