实验三

任务一

代码

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

效果

091db776d76dc027c467e8c376577973

 问题1

是组合关系。
问题2
(1) 优点:外部可直接查询窗口是否包含指定按钮,提升接口灵活性;风险:破坏类的封装性,暴露内部实现细节,增加代码耦合度和维护成本。
(2) 判断原则:用户直接需要的核心功能设为public,仅内部辅助的逻辑设为private;涉及内部实现细节设为private;可能破坏对象状态的设为private。

问题3
性能:接口1(返回const string&)无字符串拷贝,性能更高;接口2(返回const string)需拷贝字符串,性能较低。
安全性:接口1返回内部数据的引用,需注意引用生命周期;接口2返回副本,完全隔离内部数据,安全性更高。

问题4
程序可正常运行。push_back(Button(xxx))先构造临时Button对象,再移动到vector中;emplace_back(xxx)直接在vector的内存空间中构造Button对象,无临时对象开销,效率更高。

任务二

代码

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

效果

2c09ecbbee785d1fa28844afb2ef64d5

 

问题1
第一行通过vector的构造函数创建,包含5个值为42的元素;第二行通过拷贝构造函数从v1创建v2。v1和v2各包含5个值为42的数据项。

问题2
v1.size()为2,v2.size()为2,v1[0].size()为3。

问题3
能实现同等效果。区别在于at()会进行边界检查,越界抛出std::out_of_range异常;[]不进行边界检查,越界时行为未定义。

问题4
(1) 能输出-1。因为v1.at(0)返回第一个内层vector的引用,push_back(-1)在该vector末尾添加了-1,r.at(r.size()-1)可访问到该值。
(2) 优势:避免不必要的拷贝,节省内存、提升性能;限制:不能通过const引用修改对象内容。

问题5
(1) 深复制。因为拷贝后v1和v2内存独立,修改互不影响。
(2) 当v是vector<int>时,at(0)返回int&;当v是const vector<int>时,返回const int&。at()必须提供带const修饰的重载版本,否则const对象无法调用该接口。

任务三

代码

  1 #pragma once
  2 #include <iostream>
  3 #include <algorithm>
  4 
  5 // 动态int数组对象类
  6 class vectorInt {
  7 public:
  8     vectorInt();
  9     vectorInt(int n_);
 10     vectorInt(int n_, int value);
 11     vectorInt(const vectorInt &vi);
 12     ~vectorInt();
 13 
 14     int size() const;
 15     int& at(int index);
 16     const int& at(int index) const;
 17     vectorInt& assign(const vectorInt &vi);
 18 
 19     int* begin();
 20     int* end();
 21     const int* begin() const;
 22     const int* end() const;
 23 
 24 private:
 25     int n; // 当前数据项个数
 26     int *ptr; // 数据区
 27 };
 28 
 29 vectorInt::vectorInt() : n{0}, ptr{nullptr} {
 30 
 31 }
 32 
 33 vectorInt::vectorInt(int n_) : n{n_}, ptr{new int[n]} {
 34 
 35 }
 36 
 37 vectorInt::vectorInt(int n_, int value) : n{n_}, ptr{new int[n_]} {
 38     for (auto i = 0; i < n; ++i)
 39         ptr[i] = value;
 40 }
 41 
 42 vectorInt::vectorInt(const vectorInt &vi) : n{vi.n}, ptr{new int[n]} {
 43     for (auto i = 0; i < n; ++i)
 44         ptr[i] = vi.ptr[i];
 45 }
 46 
 47 vectorInt::~vectorInt() {
 48     delete[] ptr;
 49 }
 50 
 51 int vectorInt::size() const {
 52     return n;
 53 }
 54 
 55 const int& vectorInt::at(int index) const {
 56     if (index < 0 || index >= n) {
 57         std::cerr << "IndexError: index out of range\n";
 58         std::exit(1);
 59     }
 60     return ptr[index];
 61 }
 62 
 63 int& vectorInt::at(int index) {
 64     if (index < 0 || index >= n) {
 65         std::cerr << "IndexError: index out of range\n";
 66         std::exit(1);
 67     }
 68     return ptr[index];
 69 }
 70 
 71 vectorInt& vectorInt::assign(const vectorInt &vi) {
 72     if (this == &vi)
 73         return *this;
 74 
 75     int *ptr_tmp;
 76     ptr_tmp = new int[vi.n];
 77     for (int i = 0; i < vi.n; ++i)
 78         ptr_tmp[i] = vi.ptr[i];
 79 
 80     delete[] ptr;
 81     n = vi.n;
 82     ptr = ptr_tmp;
 83     return *this;
 84 }
 85 
 86 int* vectorInt::begin() {
 87     return ptr;
 88 }
 89 
 90 int* vectorInt::end() {
 91     return ptr + n;
 92 }
 93 
 94 const int* vectorInt::begin() const {
 95     return ptr;
 96 }
 97 
 98 const int* vectorInt::end() const {
 99     return ptr + n;
100 }
vectorInt.hpp
 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     std::cout << vi.at(0);
51     for (auto i = 1; i < vi.size(); ++i)
52         std::cout << ", " << vi.at(i);
53     std::cout << '\n';
54 }
55 
56 // 使用迭代器+循环输出vectorInt对象数据项
57 void output2(const vectorInt &vi) {
58     if (vi.size() == 0) {
59         std::cout << '\n';
60         return;
61     }
62     auto it = vi.begin();
63     std::cout << *it;
64     for (it = vi.begin() + 1; it != vi.end(); ++it)
65         std::cout << ", " << *it;
66     std::cout << '\n';
67 }
task2.cpp

效果

afc6cc45ef055ccb212793f456479497

 

问题1
1. 自赋值风险:未检查this == &vi,若自赋值会先释放自身ptr,再访问已释放内存,引发未定义行为。
2. 异常安全缺陷:new int[n]内存不足抛异常时,ptr已释放但n已更新,对象进入无效状态。
3. 对象完整性破坏:ptr删除后n被修改,导致对象状态不一致。


问题2
(1) static_cast<const vectorInt*>(this):将this(vectorInt*非const指针)转换为const vectorInt* const指针,目的是调用const版at函数,避免递归。
(2) const_cast<int&>:将const int&转换为int&非const引用,移除const限定,使非const版at返回可修改引用。


问题3
(1) 重载选择:v1.begin()调用非const版,适配非const对象;v2.begin()调用const版,适配const对象。
(2) 迭代器本质:是统一的容器元素访问接口,连续内存的指针也可作为迭代器,体现其抽象性,不局限于特定实现。


问题4
可行性:可使用std::fill_n和std::copy_n替代手动循环。
功能:
std::fill_n(ptr, n, value):将ptr起始的n个元素设为value。
std::copy_n(vi.ptr, vi.n, ptr):从vi.ptr拷贝vi.n个元素到ptr。
std::copy_n(vi.ptr, vi.n, ptr_tmp):从vi.ptr拷贝vi.n个元素到临时缓冲区ptr_tmp。

任务四

代码

 1 #pragma once
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstdlib>
 5 
 6 class Matrix {
 7 public:
 8     Matrix(int rows_, int cols_, double value = 0);
 9     Matrix(int rows_, double value = 0);
10     Matrix(const Matrix &x);
11     ~Matrix();
12 
13     void set(const double *pvalue, int size);
14     void clear();
15     
16     const double& at(int i, int j) const;
17     double& at(int i, int j);
18     
19     int rows() const;
20     int cols() const;
21 
22     void print() const;
23 
24 private:
25     int n_rows;
26     int n_cols;
27     double *ptr;
28 };
matrix.hpp
 1 #include "matrix.hpp"
 2 #include <cstring>
 3 
 4 Matrix::Matrix(int rows_, int cols_, double value) 
 5     : n_rows(rows_), n_cols(cols_), ptr(nullptr) {
 6     if (rows_ <= 0 || cols_ <= 0) {
 7         std::cerr << "Error: Invalid matrix dimensions\n";
 8         std::exit(1);
 9     }
10     ptr = new double[rows_ * cols_];
11     std::fill_n(ptr, n_rows * n_cols, value);
12 }
13 
14 Matrix::Matrix(int rows_, double value)
15     : Matrix(rows_, rows_, value) {}
16 
17 Matrix::Matrix(const Matrix &x)
18     : n_rows(x.n_rows), n_cols(x.n_cols), ptr(new double[x.n_rows * x.n_cols]) {
19     std::memcpy(ptr, x.ptr, n_rows * n_cols * sizeof(double));
20 }
21 
22 Matrix::~Matrix() {
23     delete[] ptr;
24 }
25 
26 void Matrix::set(const double *pvalue, int size) {
27     if (size != n_rows * n_cols) {
28         std::cerr << "Error: Size mismatch in set()\n";
29         std::exit(1);
30     }
31     std::memcpy(ptr, pvalue, size * sizeof(double));
32 }
33 
34 void Matrix::clear() {
35     std::fill_n(ptr, n_rows * n_cols, 0.0);
36 }
37 
38 const double& Matrix::at(int i, int j) const {
39     if (i < 0 || i >= n_rows || j < 0 || j >= n_cols) {
40         std::cerr << "Error: Index out of range in at()\n";
41         std::exit(1);
42     }
43     return ptr[i * n_cols + j];
44 }
45 
46 double& Matrix::at(int i, int j) {
47     return const_cast<double&>(static_cast<const Matrix*>(this)->at(i, j));
48 }
49 
50 int Matrix::rows() const {
51     return n_rows;
52 }
53 
54 int Matrix::cols() const {
55     return n_cols;
56 }
57 
58 void Matrix::print() const {
59     for (int i = 0; i < n_rows; ++i) {
60         std::cout << at(i, 0);
61         for (int j = 1; j < n_cols; ++j) {
62             std::cout << ", " << at(i, j);
63         }
64         std::cout << '\n';
65     }
66 }
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 
 9 int main() {
10     std::cout << "测试1: 普通矩阵与方阵构造\n";
11     test1();
12     std::cout << "\n测试2: 深复制与元素修改验证\n";
13     test2();
14     return 0;
15 }
16 
17 void test1() {
18     double x[1000] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
19 
20     int n, m;
21     std::cout << "Enter n and m: ";
22     std::cin >> n >> m;
23 
24     Matrix m1(n, m);
25     m1.set(x, n*m);
26     Matrix m2(m, n);
27     m2.set(x, m*n);
28     Matrix m3(n);
29     m3.set(x, n*n);
30 
31     std::cout << "矩阵对象m1: \n"; m1.print();
32     std::cout << "矩阵对象m2: \n"; m2.print();
33     std::cout << "矩阵对象m3: \n"; m3.print();
34 }
35 
36 void test2() {
37     Matrix m1(2, 3, -1);
38     const Matrix m2(m1);
39 
40     std::cout << "矩阵对象m1: \n"; m1.print();
41     std::cout << "矩阵对象m2: \n"; m2.print();
42 
43     m1.clear();
44     m1.at(0, 0) = 1;
45 
46     std::cout << "m1更新后: \n";
47     std::cout << "矩阵对象m1第0行 "; output(m1, 0);
48     std::cout << "矩阵对象m2第0行: "; output(m2, 0);
49 }
50 
51 void output(const Matrix &m, int row_index) {
52     if (row_index < 0 || row_index >= m.rows()) {
53         std::cerr << "IndexError: row index out of range\n";
54         exit(1);
55     }
56     std::cout << m.at(row_index, 0);
57     for (int j = 1; j < m.cols(); ++j) {
58         std::cout << ", " << m.at(row_index, j);
59     }
60     std::cout << '\n';
61 }
task4.cpp

效果

1025b355d18767ff2d14757ec20e0a33

 

任务五

代码

 1 #pragma once
 2 #include <iostream>
 3 #include <string>
 4 
 5 class Contact {
 6 public:
 7     Contact(const std::string& name_, const std::string& phone_);
 8     const std::string& get_name() const;
 9     const std::string& get_phone() const;
10     void display() const;
11 
12 private:
13     std::string name;
14     std::string phone;
15 };
16 
17 Contact::Contact(const std::string& name_, const std::string& phone_)
18     : name{name_}, phone{phone_} {}
19 
20 const std::string& Contact::get_name() const {
21     return name;
22 }
23 
24 const std::string& Contact::get_phone() const {
25     return phone;
26 }
27 
28 void Contact::display() const {
29     std::cout << name << ", " << phone;
30 }
contact.hpp
 1 #pragma once
 2 #include <iostream>
 3 #include <string>
 4 #include <vector>
 5 #include <algorithm>
 6 #include "contact.hpp"
 7 
 8 class ContactBook {
 9 public:
10     void add(const std::string& name, const std::string& phone);
11     void remove(const std::string& name);
12     void find(const std::string& name) const;
13     void display() const;
14     size_t size() const;
15 
16 private:
17     int index(const std::string& name) const;
18     void sort();
19 
20 private:
21     std::vector<Contact> contacts;
22 };
23 
24 void ContactBook::add(const std::string& name, const std::string& phone) {
25     if (index(name) == -1) {
26         contacts.push_back(Contact(name, phone));
27         std::cout << name << " add successfully.\n";
28         sort();
29         return;
30     }
31     std::cout << name << " already exists. fail to add!\n";
32 }
33 
34 void ContactBook::remove(const std::string& name) {
35     int idx = index(name);
36     if (idx == -1) {
37         std::cout << name << " not found, fail to remove!\n";
38         return;
39     }
40     contacts.erase(contacts.begin() + idx);
41     std::cout << name << " remove successfully.\n";
42 }
43 
44 void ContactBook::find(const std::string& name) const {
45     int idx = index(name);
46     if (idx == -1) {
47         std::cout << name << " not found!\n";
48         return;
49     }
50     contacts[idx].display();
51     std::cout << '\n';
52 }
53 
54 void ContactBook::display() const {
55     for (const auto& c : contacts) {
56         c.display();
57         std::cout << '\n';
58     }
59 }
60 
61 size_t ContactBook::size() const {
62     return contacts.size();
63 }
64 
65 int ContactBook::index(const std::string& name) const {
66     for (size_t i = 0; i < contacts.size(); ++i) {
67         if (contacts[i].get_name() == name) {
68             return static_cast<int>(i);
69         }
70     }
71     return -1;
72 }
73 
74 void ContactBook::sort() {
75     std::sort(contacts.begin(), contacts.end(),
76         [](const Contact& a, const Contact& b) {
77             return a.get_name() < b.get_name();
78         });
79 }
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     return 0;
28 }
task5.cpp

效果

 

 

 

 

 

56fb88d797b5f0eea3f47e59ff10528a

 

posted @ 2025-11-22 23:18  duser  阅读(4)  评论(1)    收藏  举报