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

实验任务1源代码:

 1 #pragma once
 2 #include <iostream>
 3 #include <string>
 4 class Button {
 5 public:
 6     Button(const std::string& label_);
 7     const std::string& get_label() const;
 8     void click();
 9 private:
10     std::string label;
11 };
12 Button::Button(const std::string& label_) : label{ label_ } {
13 }
14 
15 inline const std::string& Button::get_label() const {
16     return label;
17 }
18 inline void Button::click() {
19     std::cout << "Button '" << label << "' clicked\n";
20 }
button.hpp
 1 #pragma once
 2 #include <iostream>
 3 #include <vector>
 4 #include <algorithm>
 5 #include "button.hpp"
 6 // 窗口类
 7 class Window {
 8 public:
 9     Window(const std::string& title_);
10     void display() const;
11     void close();
12     void add_button(const std::string& label);
13     void click_button(const std::string& label);
14 private:
15     bool has_button(const std::string& label) const;
16 private:
17     std::string title;
18     std::vector<Button> buttons;
19 };
20 Window::Window(const std::string& title_) : title{ title_ } {
21     buttons.push_back(Button("close"));
22 }
23 inline void Window::display() const {
24     std::string s(40, '*');
25     std::cout << s << std::endl;
26     std::cout << "window : " << title << std::endl;
27     int cnt = 0;
28     for (const auto& button : buttons)
29         std::cout << ++cnt << ". " << button.get_label() << std::endl;
30     std::cout << s << std::endl;
31 }
32 inline void Window::close() {
33     std::cout << "close window '" << title << "'" << std::endl;
34     click_button("close");
35 }
36 
37 inline bool Window::has_button(const std::string& label) const {
38     for (const auto& button : buttons)
39         if (button.get_label() == label)
40             return true;
41 
42     return false;
43 }
44 inline void Window::add_button(const std::string& label) {
45     if (has_button(label))
46         std::cout << "button " << label << " already exists!\n";
47     else
48         buttons.push_back(Button(label));
49 }
50 inline void Window::click_button(const std::string& label) {
51     for (auto& button : buttons)
52         if (button.get_label() == label) {
53             button.click();
54             return;
55         }
56 
57     std::cout << "no button: " << label << std::endl;
58 }
window.hpp
 1 #include "window.hpp"
 2 #include <iostream>
 3 void test() {
 4     Window w("Demo");
 5     w.add_button("add");
 6     w.add_button("remove");
 7     w.add_button("modify");
 8     w.add_button("add");
 9     w.display();
10     w.close();
11 }
12 int main() {
13     std::cout << "用组合类模拟简单GUI:\n";
14     test();
15 }
task1.cpp

实验任务1运行结果:

QQ_1763799485477

  实验任务1问题1:是组合关系

  实验任务1问题2(1):会破坏类的封装性,有安全风险

  实验任务1问题2(2):如果该函数是用户和对象的主要交互方式,则应该是public,如果该函数是实现内部逻辑、内部操作而无需让外界访问或知悉则可以设为private提高安全性

  实验任务1问题3:接口一较为高效,只需返回指针,但无法对其修改,接口二则可以进行修改

  实验任务1问题4:可以运行,结果相同。前者属于构造临时对象,移动或拷贝构造之后会被销毁,后者属于原位构造,直接调用Button的构造函数,更高效

实验任务2源代码:

 1 #include <iostream>
 2 #include <vector>
 3 void test1();
 4 void test2();
 5 void output1(const std::vector<int>& v);
 6 void output2(const std::vector<int>& v);
 7 void output3(const std::vector<std::vector<int>>& v);
 8 int main() {
 9     std::cout << "深复制验证1: 标准库vector<int>\n";
10     test1();
11     std::cout << "\n深复制验证2: 标准库vector<int>嵌套使用\n";
12     test2();
13 }
14 void test1() {
15     std::vector<int> v1(5, 42);
16     const std::vector<int> v2(v1);
17 
18     std::cout << "**********拷贝构造后**********\n";
19     std::cout << "v1: "; output1(v1);
20     std::cout << "v2: "; output1(v2);
21 
22     v1.at(0) = -1;
23 
24     std::cout << "**********修改v1[0]后**********\n";
25     std::cout << "v1: "; output1(v1);
26     std::cout << "v2: "; output1(v2);
27 }
28 
29 void test2() {
30     std::vector<std::vector<int>> v1{ {1, 2, 3}, {4, 5, 6, 7} };
31     const std::vector<std::vector<int>> v2(v1);
32 
33     std::cout << "**********拷贝构造后**********\n";
34     std::cout << "v1: "; output3(v1);
35     std::cout << "v2: "; output3(v2);
36 
37     v1.at(0).push_back(-1);
38 
39     std::cout << "**********修改v1[0]后**********\n";
40     std::cout << "v1: \n";  output3(v1);
41     std::cout << "v2: \n";  output3(v2);
42 }
43 
44 // 使用xx.at()+循环输出vector<int>数据项
45 void output1(const std::vector<int>& v) {
46     if (v.size() == 0) {
47         std::cout << '\n';
48         return;
49     }
50     std::cout << v.at(0);
51     for (auto i = 1; i < v.size(); ++i)
52         std::cout << ", " << v.at(i);
53     std::cout << '\n';
54 }
55 // 使用迭代器+循环输出vector<int>数据项
56 void output2(const std::vector<int>& v) {
57     if (v.size() == 0) {
58         std::cout << '\n';
59         return;
60     }
61     auto it = v.begin();
62     std::cout << *it;
63     for (it = v.begin() + 1; it != v.end(); ++it)
64         std::cout << ", " << *it;
65     std::cout << '\n';
66 }
67 // 使用auto for分行输出vector<vector<int>>数据项
68 void output3(const std::vector<std::vector<int>>& v) {
69     if (v.size() == 0) {
70         std::cout << '\n';
71         return;
72     }
73     for (auto& i : v)
74         output2(i);
75 }
task2.cpp

实验任务2运行结果:

QQ_1763799933695

 

  实验任务2问题1:第一个创建一个包含5个初始元素的vector对象,第二个则是拷贝v1创建一个新的vector对象v2,两个都包含了5个值为42的数据项

  实验任务2问题2:v1.size()是2(有两个vector),v2.size()是2(拷贝v1,也有两个),v1[0].size()是3(包含3个int元素)

  实验任务2问题3:能实现同等效果,但用法不同,at会启用边界检查,如果超出边界则会报错,v1 [0]若超出边界则会使程序崩溃

  实验任务2问题4(1):可以,新代码访问该vector的最后一个元素

  实验任务2问题4(2):const优势在于承诺不修改所引用的对象,提高代码安全性

  实验任务2问题5(1):深复制

  实验任务2问题5(2):返回引用类型;返回常量引用;at()成员函数必须提供带const修饰的重载版本

实验任务3源代码:

 1 #include "vectorInt.hpp"
 2 #include <iostream>
 3 
 4 void test1();
 5 void test2();
 6 void output1(const vectorInt& vi);
 7 void output2(const vectorInt& vi);
 8 
 9 int main() {
10     std::cout << "测试1: \n";
11     test1();
12 
13     std::cout << "\n测试2: \n";
14     test2();
15 }
16 
17 void test1() {
18     int n;
19     std::cout << "Enter n:";
20     std::cin >> n;
21 
22     vectorInt x1(n);
23     for (auto i = 0; i < n; ++i)
24         x1.at(i) = (i + 1) * 10;
25     std::cout << "x1:"; output1(x1);
26 
27     vectorInt x2(n, 42);
28     vectorInt x3(x2);
29     x2.at(0) = -1;
30     std::cout << "x2:"; output1(x2);
31     std::cout << "x3:"; output1(x3);
32 }
33 
34 void test2() {
35     const vectorInt x(5, 42);
36     vectorInt y;
37 
38     y.assign(x);
39 
40     std::cout << "x:"; output2(x);
41     std::cout << "y:"; output2(y);
42 }
43 
44 // 使用xx.at()+循环输出vectorInt对象数据项
45 void output1(const vectorInt& vi) {
46     if (vi.size() == 0) {
47         std::cout << '\n';
48         return;
49     }
50 
51     std::cout << vi.at(0);
52     for (auto i = 1; i < vi.size(); ++i)
53         std::cout << ", " << vi.at(i);
54     std::cout << '\n';
55 }
56 
57 // 使用迭代器+循环输出vectorInt对象数据项
58 void output2(const vectorInt& vi) {
59     if (vi.size() == 0) {
60         std::cout << '\n';
61         return;
62     }
63 
64     auto it = vi.begin();
65     std::cout << *it;
66 
67     for (it = vi.begin() + 1; it != vi.end(); ++it)
68         std::cout << ", " << *it;
69     std::cout << '\n';
70 }
task3.cpp

实验任务3运行结果:

QQ_1763802353450

  实验任务3问题1:自赋值时版本2优先进行delete[] ptr,释放当前对象的数据区,会导致之后的指针悬空,原版本会检查this==&vi之后返回

  实验任务3问题2(1):通过添加const,让编译器允许我们在这个非const成员函数内调用const vectorInt版本的at成员函数。static_cast用于处理有继承关系或添加/移除常量性等相对“安全”的转换。

  实验任务3问题2(2):因为这个函数本身是非const版本的at函数,它需要返回一个非常量引用,允许调用者通过此引用修改容器中的元素。而我们从const版本的at函数得到的是一个常量引用,所以需要移除其常量性以匹配函数的返回类型。

  实验任务3问题3:编译器会选择最匹配的版本,即v1.begin()匹配非const版本,v2.begin()匹配const版本。const重载用于常量对象,返回的迭代器是常量指针,防止通过迭代器修改所指元素;非const重载用于非常量对象,允许修改。

  实验任务3问题4:可以,在功能上与原本的代码完全相同,但更新后的代码更加清晰且高效fill_n是从指针ptr指向的位置开始,将n个连续的元素都赋值为value,即用指定的值填充一个范围内的元素。copy_n是从源指针vi.ptr开始,精确拷贝连续的vi.n个元素,到目标指针ptr所指向的内存位置,即元素的逐个拷贝。

实验任务4源代码:

 1 #pragma once
 2 
 3 // 类Matrix声明
 4 class Matrix {
 5 public:
 6     Matrix(int rows_, int cols_, double value = 0);  // 构造rows_*cols_矩阵对象, 初值value
 7     Matrix(int rows_, double value = 0);  // 构造rows_*rows_方阵对象, 初值value
 8     Matrix(const Matrix& x);  // 深复制
 9     ~Matrix();
10 
11     void set(const double* pvalue, int size);   // 按行复制pvalue指向的数据,要求size = rows * cols, 否则报错退出
12     void clear();   // 矩阵对象数据项置0
13 
14     const double& at(int i, int j) const;   // 返回矩阵对象索引(i,j)对应的数据项const引用(越界则报错后退出)
15     double& at(int i, int j);   // 返回矩阵对象索引(i,j)对应的数据项引用(越界则报错后退出)
16 
17     int rows() const;   // 返回矩阵对象行数
18     int cols() const;   // 返回矩阵对象列数
19 
20     void print() const;   // 按行打印数据
21 
22 private:
23     int n_rows;
24     int n_cols;
25     double* ptr;
26 };
matrix.hpp
  1 #include "matrix.hpp"
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstring>
  5 
  6 // 构造函数:构造矩阵对象
  7 Matrix::Matrix(int rows_, int cols_, double value): n_rows(rows_), n_cols(cols_), ptr(nullptr) {
  8     if (rows_ <= 0 || cols_ <= 0) {
  9         std::cerr << "Error" << std::endl;
 10         std::exit(EXIT_FAILURE);
 11     }
 12 
 13     ptr = new double[rows_ * cols_];
 14 
 15     for (int i = 0; i < rows_ * cols_; ++i) {
 16         ptr[i] = value;
 17     }
 18 }
 19 
 20 // 构造函数:构造方阵对象
 21 Matrix::Matrix(int rows_, double value)
 22     : n_rows(rows_), n_cols(rows_), ptr(nullptr) {
 23     if (rows_ <= 0) {
 24         std::cerr << "Error:输入数据有误!" << std::endl;
 25         std::exit(EXIT_FAILURE);
 26     }
 27 
 28     ptr = new double[rows_ * rows_];
 29 
 30     for (int i = 0; i < rows_ * rows_; ++i) {
 31         ptr[i] = value;
 32     }
 33 }
 34 
 35 // 拷贝构造函数:深复制
 36 Matrix::Matrix(const Matrix& x)
 37     : n_rows(x.n_rows), n_cols(x.n_cols), ptr(nullptr) {
 38     if (x.ptr != nullptr) {
 39         ptr = new double[n_rows * n_cols];
 40         std::memcpy(ptr, x.ptr, n_rows * n_cols * sizeof(double));
 41     }
 42 }
 43 
 44 Matrix::~Matrix() {
 45     delete[] ptr;
 46 }
 47 
 48 void Matrix::set(const double* pvalue, int size) {
 49     if (size != n_rows * n_cols) {
 50         std::cerr << "Error:输入数据不符!" << std::endl;
 51         std::cerr << "Expected: " << n_rows * n_cols << ", Got: " << size << std::endl;
 52         std::exit(EXIT_FAILURE);
 53     }
 54 
 55     if (pvalue == nullptr) {
 56         std::cerr << "Error" << std::endl;
 57         std::exit(EXIT_FAILURE);
 58     }
 59 
 60     std::memcpy(ptr, pvalue, size * sizeof(double));
 61 }
 62 
 63 // 矩阵对象数据项置0
 64 void Matrix::clear() {
 65     for (int i = 0; i < n_rows * n_cols; ++i) {
 66         ptr[i] = 0.0;
 67     }
 68 }
 69 
 70 const double& Matrix::at(int i, int j) const {
 71     if (i < 0 || i >= n_rows || j < 0 || j >= n_cols) {
 72         std::cerr << "Error: 数据溢出!" << std::endl;
 73         std::cerr << "Requested: (" << i << ", " << j << "), ";
 74         std::cerr << "Dimensions: " << n_rows << " x " << n_cols << std::endl;
 75         std::exit(EXIT_FAILURE);
 76     }
 77 
 78     return ptr[i * n_cols + j];
 79 }
 80 
 81 // 返回矩阵对象索引(i,j)对应的数据项引用
 82 double& Matrix::at(int i, int j) {
 83     return const_cast<double&>(static_cast<const Matrix&>(*this).at(i, j));
 84 }
 85 
 86 int Matrix::rows() const {
 87     return n_rows;
 88 }
 89 
 90 int Matrix::cols() const {
 91     return n_cols;
 92 }
 93 
 94 void Matrix::print() const {
 95     for (int i = 0; i < n_rows; ++i) {
 96         for (int j = 0; j < n_cols; ++j) {
 97             std::cout << ptr[i * n_cols + j];
 98             if (j < n_cols - 1) {
 99                 std::cout << ", ";
100             }
101         }
102         std::cout << std::endl;
103     }
104 }
matrix.cpp
 1 #include <iostream>
 2 #include <cstdlib>
 3 #include "matrix.hpp"
 4 
 5 void test1();
 6 void test2();
 7 void output(const Matrix& m, int row_index);
 8 int main() {
 9     std::cout << "测试1: \n";
10     test1();
11 
12     std::cout << "\n测试2: \n";
13     test2();
14 }
15 
16 void test1() {
17     double x[1000] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
18 
19     int n, m;
20     std::cout << "Enter n and m: ";
21     std::cin >> n >> m;
22 
23     Matrix m1(n, m);  // 创建矩阵对象m1,大小n*m
24     m1.set(x, n * m);  // 用一维数组x的值按行为矩阵m1赋值
25 
26     Matrix m2(m, n);  // 创建矩阵对象m2,大小m*n
27     m2.set(x, m * n);  // 用一维数组x的值按行为矩阵m1赋值
28 
29     Matrix m3(n);  // 创建一个n*n方阵对象
30     m3.set(x, n * n);  // 用一维数组x的值按行为矩阵m3赋值
31 
32     std::cout << "矩阵对象m1:\n";  m1.print();
33     std::cout << "矩阵对象m2:\n";  m2.print();
34     std::cout << "矩阵对象m3:\n";  m3.print();
35 }
36 
37 void test2() {
38     Matrix m1(2, 3, -1);
39     const Matrix m2(m1);
40 
41     std::cout << "矩阵对象m1:\n";  m1.print();
42     std::cout << "矩阵对象m2:\n";  m2.print();
43 
44     m1.clear();
45     m1.at(0, 0) = 1;
46 
47     std::cout << "m1更新后:\n";
48     std::cout << "矩阵对象m1第0行:";  output(m1, 0);
49     std::cout << "矩阵对象m2第0行:";  output(m2, 0);
50 }
51 
52 // 输出矩阵对象row_index行所有元素
53 void output(const Matrix& m, int row_index) {
54     if (row_index < 0 || row_index > m.rows()) {
55         std::cerr << "IndexError:row index out of range\n";
56         std::exit(1);
57     }
58 
59     std::cout << m.at(row_index, 0);
60     for (int j = 1; j < m.cols(); ++j)
61         std::cout << ", " << m.at(row_index, j);
62     std::cout << '\n';
63 }
task4.cpp

实验任务4运行结果:

QQ_1763884026350

 

实验任务5源代码:

 1 #pragma once
 2 #include <iostream>
 3 #include <string>
 4 
 5 // 联系人类
 6 class Contact {
 7 public:
 8     Contact(const std::string& name_, const std::string& phone_);
 9     const std::string& get_name() const;
10     const std::string& get_phone() const;
11     void display() const;
12 
13 private:
14     std::string name;// 必填项
15     std::string phone;   // 必填项
16 };
17 
18 Contact::Contact(const std::string& name_, const std::string& phone_) :name{ name_ },
19 phone{ phone_ } {
20 }
21 
22 const std::string& Contact::get_name() const {
23     return name;
24 }
25 
26 const std::string& Contact::get_phone() const {
27     return phone;
28 }
29 
30 void Contact::display() const {
31     std::cout << name << ", " << phone;
32 }
contact.hpp
 1 #pragma  once
 2 
 3 #include <iostream>
 4 #include <string>
 5 #include <vector>
 6 #include <algorithm>
 7 #include "contact.hpp"
 8 
 9 // 通讯录类
10 class ContactBook {
11 public:
12     void add(const std::string& name, const std::string& phone); // 添加联系人
13     void remove(const std::string& name); // 移除联系人
14     void find(const std::string& name) const; // 查找联系人
15     void display() const; // 显示所有联系人
16     size_t size() const;
17 
18 private:
19     int index(const std::string& name) const;  // 返回联系人在contacts内索引,如不存在,返回-1
20     void sort(); // 按姓名字典序升序排序通讯录
21 
22 private:
23     std::vector<Contact> contacts;
24 };
25 
26 void ContactBook::add(const std::string& name, const std::string& phone) {
27     if (index(name) == -1) {
28         contacts.push_back(Contact(name, phone));
29         std::cout << name << " add successfully.\n";
30         sort();
31         return;
32     }
33 
34     std::cout << name << " already exists. fail to add!\n";
35 }
36 
37 void ContactBook::remove(const std::string& name) {
38     int i = index(name);
39 
40     if (i == -1) {
41         std::cout << name << " not found, fail to remove!\n";
42         return;
43     }
44 
45     contacts.erase(contacts.begin() + i);
46     std::cout << name << " remove successfully.\n";
47 }
48 
49 void ContactBook::find(const std::string& name) const {
50     int i = index(name);
51 
52     if (i == -1) {
53         std::cout << name << " not found!\n";
54         return;
55     }
56 
57     contacts[i].display();
58     std::cout << '\n';
59 }
60 
61 void ContactBook::display() const {
62     for (auto& c : contacts) {
63         c.display();
64         std::cout << '\n';
65     }
66 }
67 
68 size_t ContactBook::size() const {
69     return contacts.size();
70 }
71 
72 // 查找指定姓名的联系人
73 int ContactBook::index(const std::string& name) const {
74     for (size_t i = 0; i < contacts.size(); ++i) {
75         if (contacts[i].get_name() == name) {
76             return static_cast<int>(i);
77         }
78     }
79 
80     return -1;
81 }
82 
83 // 按姓名字典序升序排列
84 void ContactBook::sort() {
85     for (size_t i = 0; i < contacts.size(); ++i) {
86         for (size_t j = 0; j < contacts.size() - i - 1; j++) {
87             if (contacts[j].get_name() > contacts[j + 1].get_name()) {
88                 Contact temp = contacts[j];
89                 contacts[j] = contacts[j + 1];
90                 contacts[j + 1] = temp;
91             }
92         }
93     }
94 }
contactBook.hpp
 1 #include "contactBook.hpp"
 2 
 3 void test() {
 4     ContactBook contactbook;
 5 
 6     std::cout << "1. add contacts\n";
 7     contactbook.add("Bob", "18199357253");
 8     contactbook.add("Alice", "17300886371");
 9     contactbook.add("Linda", "18184538072");
10     contactbook.add("Alice", "17300886371");
11 
12     std::cout << "\n2. display contacts\n";
13     std::cout << "There are " << contactbook.size() << " contacts.\n";
14     contactbook.display();
15 
16     std::cout << "\n3. find contacts\n";
17     contactbook.find("Bob");
18     contactbook.find("David");
19 
20     std::cout << "\n4. remove contact\n";
21     contactbook.remove("Bob");
22     contactbook.remove("David");
23 }
24 
25 int main() {
26     test();
27 }
task5.cpp

 

实验任务5运行结果:

QQ_1763899845605

 

posted on 2025-11-24 20:12  豪雅  阅读(6)  评论(0)    收藏  举报