实验三
任务一
代码
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 }
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 }
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 }
效果

问题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 }
效果

问题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 }
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 }
效果

问题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 };
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 }
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 }
效果

任务五
代码
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 }
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 }
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 }
效果

浙公网安备 33010602011771号