实验三
实验任务一:
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 inline const std::string& Button::get_label() const { 18 return label; 19 } 20 21 inline void Button::click() { 22 std::cout << "Button '" << label << "' clicked\n"; 23 }
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 15 private: 16 bool has_button(const std::string &label) const; 17 std::string title; 18 std::vector<Button> buttons; 19 }; 20 21 Window::Window(const std::string &title_): title{title_} { 22 buttons.push_back(Button("close")); 23 } 24 25 inline void Window::display() const { 26 std::string s(40, '*'); 27 std::cout << s << std::endl; 28 std::cout << "window: " << title << std::endl; 29 int cnt = 0; 30 for(const auto &button: buttons) 31 std::cout << ++cnt << ". " << button.get_label() << std::endl; 32 std::cout << s << std::endl; 33 } 34 35 inline void Window::close() { 36 std::cout << "close window '" << title << "'" << std::endl; 37 click_button("close"); 38 } 39 40 inline bool Window::has_button(const std::string &label) const { 41 for(const auto &button: buttons) 42 if(button.get_label() == label) 43 return true; 44 return false; 45 } 46 47 inline void Window::add_button(const std::string &label) { 48 if(has_button(label)) 49 std::cout << "button " << label << " already exists!\n"; 50 else 51 buttons.push_back(Button(label)); 52 } 53 54 inline void Window::click_button(const std::string &label) { 55 for(auto &button: buttons) 56 if(button.get_label() == label) { 57 button.click(); 58 return; 59 } 60 std::cout << "no button: " << label << std::endl; 61 }
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.是否为外部必需的功能:若函数是类对外提供的核心服务,需设为 public;若仅为类内部其他成员函数提供辅助,则设为 private;
2.是否涉及内部实现细节:若函数依赖类的内部存储结构、逻辑,设为 private,避免外部依赖导致内部实现无法灵活修改;
问题三:
接口 1:无拷贝,性能高;const限制不可改,安全性高。
接口 2:值返回需拷贝,性能低;仅不影响原对象,安全性一般。
问题四:能正常运行,差别:push_back先创临时对象再拷贝,有额外步骤和空间;emplace_back直接在容器内构造,无临时对象,性能更优。
实验任务二:
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 9 int main() { 10 std::cout << "深复制验证1: 标准库vector<int>\n"; 11 test1(); 12 std::cout << "\n深复制验证2: 标准库vector<int>嵌套使用\n"; 13 test2(); 14 } 15 16 void test1() { 17 std::vector<int> v1(5, 42); 18 const std::vector<int> v2(v1); 19 std::cout << "**********拷贝构造后**********\n"; 20 std::cout << "v1: "; output1(v1); 21 std::cout << "v2: "; output1(v2); 22 v1.at(0) = -1; 23 std::cout << "**********修改v1[0]后**********\n"; 24 std::cout << "v1: "; output1(v1); 25 std::cout << "v2: "; output1(v2); 26 } 27 28 void test2() { 29 std::vector<std::vector<int>> v1{{1, 2, 3}, {4, 5, 6, 7}}; 30 const std::vector<std::vector<int>> v2(v1); 31 std::cout << "**********拷贝构造后**********\n"; 32 std::cout << "v1: "; output3(v1); 33 std::cout << "v2: "; output3(v2); 34 v1.at(0).push_back(-1); 35 std::cout << "**********修改v1[0]后**********\n"; 36 std::cout << "v1: \n"; output3(v1); 37 std::cout << "v2: \n"; output3(v2); 38 } 39 40 void output1(const std::vector<int> &v) { 41 if(v.size() == 0) {std::cout << '\n'; return;} 42 std::cout << v.at(0); 43 for(auto i = 1; i < v.size(); ++i) 44 std::cout << ", " << v.at(i); 45 std::cout << '\n'; 46 } 47 48 void output2(const std::vector<int> &v) { 49 if(v.size() == 0) {std::cout << '\n'; return;} 50 auto it = v.begin(); 51 std::cout << *it; 52 for(it = v.begin()+1; it != v.end(); ++it) 53 std::cout << ", " << *it; 54 std::cout << '\n'; 55 } 56 57 void output3(const std::vector<std::vector<int>>& v) { 58 if(v.size() == 0) {std::cout << '\n'; return;} 59 for(auto &i: v) output2(i); 60 }
运行结果:

问题1:第一行调用 vector 的 “带大小和初值” 的构造函数,创建含 5 个元素的 vector,每个元素初值为 42,v1含 5 个 42。
第二行调用 vector 的拷贝构造函数,用v1的值创建v2,v2同样含 5 个 42。
问题2:v1.size()=2;v2.size()=2;v1[0].size()=3。
问题3:能,at()会做越界检查,[ ]不做检查。
问题4:(1):能。v1.at(0)返回内层 vector 的引用,r绑定该引用,r.size()-1对应-1的索引,可输出。
(2):优势:无需拷贝内层 vector,节省内存;限制:仅能读取r指向的内容,无法修改。
问题5:(1):深复制。修改v1(或嵌套 vector)不影响v2,两者数据独立。
(2):非 constvector<int>的at()返回int&,constvector<int>的at()返回const int&;必须提供 const 重载,否则 const 对象无法调用at()读取元素。
实验任务三:
1 #pragma once 2 #include <iostream> 3 #include <algorithm> 4 5 class vectorInt { 6 public: 7 vectorInt(); 8 vectorInt(int n_); 9 vectorInt(int n_, int value); 10 vectorInt(const vectorInt &vi); 11 ~vectorInt(); 12 int size() const; 13 int& at(int index); 14 const int& at(int index) const; 15 vectorInt& assign(const vectorInt &vi); 16 int* begin(); 17 int* end(); 18 const int* begin() const; 19 const int* end() const; 20 21 private: 22 int n; 23 int* ptr; 24 }; 25 26 vectorInt::vectorInt() : n{0}, ptr{nullptr} {} 27 vectorInt::vectorInt(int n_) : n{n_}, ptr{new int[n]} {} 28 vectorInt::vectorInt(int n_, int value) : n{n_}, ptr{new int[n_]} { 29 std::fill_n(ptr, n, value); 30 } 31 vectorInt::vectorInt(const vectorInt &vi) : n{vi.n}, ptr{new int[n]} { 32 std::copy_n(vi.ptr, vi.n, ptr); 33 } 34 vectorInt::~vectorInt() { delete[] ptr; } 35 36 int vectorInt::size() const { return n; } 37 38 const int& vectorInt::at(int index) const { 39 if (index < 0 || index >= n) { 40 std::cerr << "IndexError: index out of range\n"; 41 std::exit(1); 42 } 43 return ptr[index]; 44 } 45 int& vectorInt::at(int index) { 46 return const_cast<int&>(static_cast<const vectorInt*>(this)->at(index)); 47 } 48 49 vectorInt& vectorInt::assign(const vectorInt &vi) { 50 if (this == &vi) return *this; 51 int* ptr_tmp = new int[vi.n]; 52 std::copy_n(vi.ptr, vi.n, ptr_tmp); 53 delete[] ptr; 54 n = vi.n; 55 ptr = ptr_tmp; 56 return *this; 57 } 58 59 int* vectorInt::begin() { return ptr; } 60 int* vectorInt::end() { return ptr + n; } 61 const int* vectorInt::begin() const { return ptr; } 62 const int* vectorInt::end() const { return ptr + n; }
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 std::cout << "\n测试2: \n"; 13 test2(); 14 } 15 16 void test1() { 17 int n; 18 std::cout << "Enter n: "; 19 std::cin >> n; 20 vectorInt x1(n); 21 for (auto i = 0; i < n; ++i) x1.at(i) = (i + 1) * 10; 22 std::cout << "x1: "; output1(x1); 23 24 vectorInt x2(n, 42); 25 vectorInt x3(x2); 26 x2.at(0) = -1; 27 std::cout << "x2: "; output1(x2); 28 std::cout << "x3: "; output1(x3); 29 } 30 31 void test2() { 32 const vectorInt x(5, 42); 33 vectorInt y; 34 y.assign(x); 35 std::cout << "x: "; output2(x); 36 std::cout << "y: "; output2(y); 37 } 38 39 void output1(const vectorInt &vi) { 40 if (vi.size() == 0) { std::cout << '\n'; return; } 41 std::cout << vi.at(0); 42 for (auto i = 1; i < vi.size(); ++i) 43 std::cout << ", " << vi.at(i); 44 std::cout << '\n'; 45 } 46 47 void output2(const vectorInt &vi) { 48 if (vi.size() == 0) { std::cout << '\n'; return; } 49 auto it = vi.begin(); 50 std::cout << *it; 51 for (it = vi.begin() + 1; it != vi.end(); ++it) 52 std::cout << ", " << *it; 53 std::cout << '\n'; 54 }
运行结果:

问题1:差异化代码:1.安全版本有自赋值检查:if (this == &vi) return *this;另一个无。当调用obj.assign(obj)(自赋值)时,版本 2 会直接执行delete[] ptr;,释放当前对象的内存。后续执行ptr = new int[n];时,虽然能分配新内存,但vi.ptr(即obj.ptr)已被释放,属于访问无效内存,会导致程序崩溃或数据乱码;而安全版本通过if (this == &vi)直接返回,完全规避该问题
2.版本1先分配新内存:int* ptr_tmp = new int[vi.n]; → 复制数据 → 再释放旧内存:delete[] ptr; 版本2先释放旧内存:delete[] ptr; → 再分配新内存:ptr = new int[n];先释放旧内存再分配新内存:若新内存分配失败,会造成内存泄漏,且 ptr 变为野指针;无临时内存缓冲:数据复制过程中出现异常会导致数据丢失,安全版本通过先分配临时内存、复制数据后释放旧内存,避免上述问题。
问题2:(1):这步转换将当前对象的指针(this,类型为 vectorInt*)强制转换为指向const对象的指针(类型为 const vectorInt*)。这里的目的就是为了复用 const 版本的 at() 方法中的核心逻辑(主要是越界检查),避免在非const版本中重复编写相同的代码。
(2):这步转换将 const int& 类型(const版本at()的返回值)“去除const属性”,转换成 int& 类型。用const_cast来移除const限制,以满足非const版本at()的功能需求,同时又能复用const版本的代码。
问题3:(1):v1:int* begin();v2:const int* begin() const;对于非 const 对象(如vectorInt v1(5);):调对于非 const 对象(如vectorInt v1(5);):调用int* begin();。适配场景:外部需要修改元素(如*v1.begin() = 10;),返回的非 const 指针支持读写操作;对于 const 对象(如const vectorInt v2(5);):调用const int* begin() const;。适配场景:外部仅需读取元素(如std::cout << *v2.begin();),返回的 const 指针仅支持读操作,避免 const 对象被意外修改,符合 “const 正确性” 原则。
问题4:
1.std::fill_n(ptr, n, value);
功能:从ptr指向的内存开始,连续填充n个int类型元素,每个元素的值均为value;
替代的手动循环:for(auto i = 0; i < n; ++i) ptr[i] = value;;
优势:代码更简洁,避免手动循环的索引管理错误,且标准库算法经过优化,效率更高。
2.std::copy_n(vi.ptr, vi.n, ptr);
功能:从vi.ptr指向的内存开始,复制vi.n个int类型元素,将其依次存储到ptr指向的内存中;
替代的手动循环:for(int i = 0; i < vi.n; ++i) ptr[i] = vi.ptr[i];;
优势:自动处理元素复制,支持任意连续内存的拷贝,扩展性更强(如后续将int改为其他类型,无需修改循环逻辑)。
3.std::copy_n(vi.ptr, vi.n, ptr_tmp);
功能:与拷贝构造中的std::copy_n一致,从vi.ptr复制vi.n个元素到ptr_tmp(临时内存);
作用:确保新内存中的数据与vi完全一致后,再替换旧内存的ptr,保证赋值过程中数据的完整性,同时避免手动循环的冗余代码。
实验任务四:
#pragma once #include <iostream> #include <cstdlib> class Matrix { public: Matrix(int rows_, int cols_, double value = 0) : n_rows(rows_), n_cols(cols_) { if (rows_ <= 0 || cols_ <= 0) { std::cerr << "Error: Invalid matrix size\n"; exit(1); } ptr = new double[rows_ * cols_]; for (int i = 0; i < rows_ * cols_; ++i) { ptr[i] = value; } } Matrix(int rows_, double value = 0) : n_rows(rows_), n_cols(rows_) { if (rows_ <= 0) { std::cerr << "Error: Invalid square matrix size\n"; exit(1); } ptr = new double[rows_ * rows_]; for (int i = 0; i < rows_ * rows_; ++i) { ptr[i] = value; } } Matrix(const Matrix &x) : n_rows(x.n_rows), n_cols(x.n_cols) { ptr = new double[n_rows * n_cols]; for (int i = 0; i < n_rows * n_cols; ++i) { ptr[i] = x.ptr[i]; } } ~Matrix() { delete[] ptr; } void set(const double *pvalue, int size) { if (size != n_rows * n_cols) { std::cerr << "Error: Size mismatch\n"; exit(1); } for (int i = 0; i < size; ++i) { ptr[i] = pvalue[i]; } } void clear() { for (int i = 0; i < n_rows * n_cols; ++i) { ptr[i] = 0.0; } } const double& at(int i, int j) const { if (i < 0 || i >= n_rows || j < 0 || j >= n_cols) { std::cerr << "Error: Index out of range\n"; exit(1); } return ptr[i * n_cols + j]; } double& at(int i, int j) { if (i < 0 || i >= n_rows || j < 0 || j >= n_cols) { std::cerr << "Error: Index out of range\n"; exit(1); } return ptr[i * n_cols + j]; } int rows() const { return n_rows; } int cols() const { return n_cols; } void print() const { for (int i = 0; i < n_rows; ++i) { for (int j = 0; j < n_cols; ++j) { std::cout << ptr[i * n_cols + j]; if (j != n_cols - 1) std::cout << ", "; } std::cout << "\n"; } } private: int n_rows; int n_cols; double *ptr; };
#include <iostream> #include <cstdlib> #include "matrix.hpp" void test1(); void test2(); void output(const Matrix &m, int row_index); int main() { std::cout << "测试1: \n"; test1(); std::cout << "\n测试2: \n"; test2(); return 0; } void test1() { double x[1000] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int n, m; std::cout << "Enter n and m: "; std::cin >> n >> m; Matrix m1(n, m); m1.set(x, n*m); Matrix m2(m, n); m2.set(x, m*n); Matrix m3(n); m3.set(x, n*n); std::cout << "矩阵对象m1: \n"; m1.print(); std::cout << "矩阵对象m2: \n"; m2.print(); std::cout << "矩阵对象m3: \n"; m3.print(); } void test2() { Matrix m1(2, 3, -1); const Matrix m2(m1); std::cout << "矩阵对象m1: \n"; m1.print(); std::cout << "矩阵对象m2: \n"; m2.print(); m1.clear(); m1.at(0, 0) = 1; std::cout << "m1更新后: \n"; std::cout << "矩阵对象m1第0行: "; output(m1, 0); std::cout << "矩阵对象m2第0行: "; output(m2, 0); } void output(const Matrix &m, int row_index) { if (row_index < 0 || row_index >= m.rows()) { std::cerr << "IndexError: row index out of range\n"; std::exit(1); } std::cout << m.at(row_index, 0); for (int j = 1; j < m.cols(); ++j) { std::cout << ", " << m.at(row_index, j); } std::cout << '\n'; }
运行结果:

实验任务五:
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_): name{name_}, phone{phone_} {} 18 19 const std::string& Contact::get_name() const { 20 return name; 21 } 22 23 const std::string& Contact::get_phone() const { 24 return phone; 25 } 26 27 void Contact::display() const { 28 std::cout << name << ", " << phone; 29 }
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 std::vector<Contact> contacts; 20 }; 21 22 void ContactBook::add(const std::string &name, const std::string &phone) { 23 if (index(name) == -1) { 24 contacts.push_back(Contact(name, phone)); 25 std::cout << name << " add successfully.\n"; 26 sort(); 27 return; 28 } 29 std::cout << name << " already exists. fail to add!\n"; 30 } 31 32 void ContactBook::remove(const std::string &name) { 33 int i = index(name); 34 if (i == -1) { 35 std::cout << name << " not found, fail to remove!\n"; 36 return; 37 } 38 contacts.erase(contacts.begin() + i); 39 std::cout << name << " remove successfully.\n"; 40 } 41 42 void ContactBook::find(const std::string &name) const { 43 int i = index(name); 44 if (i == -1) { 45 std::cout << name << " not found!\n"; 46 return; 47 } 48 contacts[i].display(); 49 std::cout << '\n'; 50 } 51 52 void ContactBook::display() const { 53 for (auto &c : contacts) { 54 c.display(); 55 std::cout << '\n'; 56 } 57 } 58 59 size_t ContactBook::size() const { 60 return contacts.size(); 61 } 62 63 // 补足1:实现index函数,查找姓名对应的索引 64 int ContactBook::index(const std::string &name) const { 65 for (size_t i = 0; i < contacts.size(); ++i) { 66 if (contacts[i].get_name() == name) { 67 return static_cast<int>(i); 68 } 69 } 70 return -1; 71 } 72 73 // 补足2:实现sort函数,按姓名字典序升序排序 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 ); 80 }
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号