实验3
实验任务1:
源代码Button.hpp
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 inline const std::string &Button::get_label() const{ 19 return label; 20 } 21 22 inline void Button::click(){ 23 std::cout << "Button '" << label << "' clicked\n"; 24 }
源代码window.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 15 private: 16 bool has_button(const std::string &label) const; 17 private: 18 std::string title; 19 std::vector<Button> buttons; 20 }; 21 22 window::window(const std::string &title_):title{title_}{ 23 buttons.emplace_back(Button("close")); 24 } 25 26 inline void window::display() const { 27 std::string s(40,'*'); 28 std::cout << s << std::endl; 29 std::cout << "window;" << title << std::endl; 30 int cnt=0; 31 for(const auto &button:buttons) 32 std::cout << ++cnt << ". " << button.get_label() << std::endl; 33 std::cout << s << std::endl; 34 } 35 36 inline void window::close(){ 37 std::cout << "close window '" << title << "'" << std::endl; 38 click_button("close"); 39 } 40 41 inline bool window::has_button(const std::string &label) const { 42 for(const auto &button:buttons) 43 if(button.get_label()==label) 44 return true; 45 return false; 46 } 47 48 inline void window::add_button(const std::string &label){ 49 if(has_button(label)) 50 std::cout << "button " << label << " already exists!\n"; 51 else 52 buttons.emplace_back(Button(label)); 53 } 54 55 inline void window::click_button(const std::string &label){ 56 for(auto &button:buttons) 57 if(button.get_label()==label){ 58 button.click(); 59 return; 60 } 61 std::cout << "no button: " << label <<std::endl; 62 }
源代码task1.cpp
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:
是组合关系。window对象包含Button对象,Button不存在独立于window的生命周期。
问题2:
(1)优点:用户可以直接查询按钮是否存在,增加灵活性。
风险:破坏了封装性,增加维护成本,后续若修改判断逻辑,可能影响依赖该接口的外部代码。
(2)用户直接需要的功能设为public,仅为内部辅助功能的设为private,可能破坏对象一致性的操作必须设为private。
问题3:
接口1:性能:返回引用,避免字符串拷贝,性能更好。
安全性:调用者可能保存引用并在Button对象销毁后使用。
接口2:性能:返回值拷贝,有性能开销,特别是长字符串时。
安全性:调用者获得独立副本。
问题4:
修改后程序正常运行。push_back先构造临时Button对象,再拷贝或移动到vector中,涉及两次构造。emplace_back直接在vector内存中构造Button对象,只涉及一次构造,更高效,避免了不必要的拷贝。
实验任务2:
源代码task2.cpp
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 void output1(const std::vector<int> &v){ 48 if(v.size()==0){ 49 std::cout << '\n'; 50 return; 51 } 52 std::cout << v.at(0); 53 for(auto i=1;i<v.size();++i) 54 std::cout << ", " <<v.at(i); 55 std::cout << '\n'; 56 } 57 58 void output2(const std::vector<int> &v){ 59 if(v.size()==0){ 60 std::cout << '\n'; 61 return; 62 } 63 64 auto it=v.begin(); 65 std::cout << *it; 66 67 for(it=v.begin()+1;it!=v.end();++it) 68 std::cout << ", " << *it; 69 std::cout << '\n'; 70 } 71 72 void output3(const std::vector<std::vector<int>>& v){ 73 if(v.size()==0){ 74 std::cout << '\n'; 75 return; 76 } 77 for(auto &i:v) 78 output2(i); 79 }
运行结果截图:

问题1:
std::vector<int> v1(5,42);完成默认构造,v1包含5个值为42的数据项;
const std::vector<int> v2(v1);完成拷贝构造,v2包含5个值为42的数据项。
问题2:
v1.size()=2;v2.size()=2;v1[0].size()=3.
问题3:
能。区别:v1.at(0)会进行边界检查,v1[0]不进行边界检查。
问题4:
(1)能。v1.at(0)返回第一个内层vector的引用,push_back(-1)在该末尾添加-1,r.at(r.size()-1)访问最后一个元素,即刚添加的-1.
(2)优势:避免不必要的拷贝,节省内存。限制:不能通过r修改vector的内容。
问题5:
(1)深复制。
(2)当v是vector<int>时,v.at(0)返回int&;当v是const vector<int>时,v.at(0)返回const int&.at()必须携带const修饰的重载版本。
实验任务3:
源代码vectorlnt.hpp
1 #pragma once 2 3 #include<iostream> 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 13 int size() const; 14 int& at(int index); 15 const int& at(int index) const; 16 vectorInt& assign(const vectorInt &vi); 17 18 int* begin(); 19 int* end(); 20 const int* begin() const; 21 const int* end() const; 22 private: 23 int n; 24 int *ptr; 25 }; 26 27 vectorInt::vectorInt():n{0},ptr{nullptr}{ 28 } 29 30 vectorInt::vectorInt(int n_):n{n_},ptr{new int[n_]}{ 31 } 32 33 vectorInt::vectorInt(int n_,int value):n{n_},ptr{new int[n_]}{ 34 for(auto i=0;i<n;++i) 35 ptr[i]=value; 36 } 37 38 vectorInt::vectorInt(const vectorInt &vi):n{vi.n},ptr{new int[n]}{ 39 for(auto i=0;i<n;i++) 40 ptr[i]=vi.ptr[i]; 41 } 42 43 vectorInt::~vectorInt(){ 44 delete [] ptr; 45 } 46 47 int vectorInt::size() const{ 48 return n; 49 } 50 51 const int& vectorInt::at(int index) const{ 52 if(index<0||index>=n){ 53 std::cerr << "IndexError:index out of range\n"; 54 std::exit(1); 55 } 56 57 return ptr[index]; 58 } 59 60 int& vectorInt::at(int index){ 61 if(index<0||index>=n){ 62 std::cerr << "IndexError:index out of range\n"; 63 std::exit(1); 64 } 65 66 return ptr[index]; 67 } 68 69 vectorInt& vectorInt::assign(const vectorInt &vi){ 70 if(this==&vi) 71 return *this; 72 int *ptr_tmp; 73 ptr_tmp=new int[vi.n]; 74 for(int i=0;i<vi.n;++i) 75 ptr_tmp[i]=vi.ptr[i]; 76 delete[] ptr; 77 n=vi.n; 78 ptr=ptr_tmp; 79 return *this; 80 } 81 82 int* vectorInt::begin(){ 83 return ptr; 84 } 85 86 int* vectorInt::end(){ 87 return ptr+n; 88 } 89 90 const int* vectorInt::begin() const{ 91 return ptr; 92 } 93 94 const int* vectorInt::end() const{ 95 return ptr+n; 96 }
源代码task3.cpp
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 void output1(const vectorInt &vi){ 45 if(vi.size()==0){ 46 std::cout << '\n'; 47 return; 48 } 49 std::cout << vi.at(0); 50 for(auto i=1;i<vi.size();++i) 51 std::cout << ", " << vi.at(i); 52 std::cout << '\n'; 53 } 54 55 void output2(const vectorInt &vi){ 56 if(vi.size()==0){ 57 std::cout << '\n'; 58 return; 59 } 60 61 auto it=vi.begin(); 62 std::cout << *it; 63 64 for(it=vi.begin()+1;it!=vi.end();++it) 65 std::cout << ", " << *it; 66 std::cout << '\n'; 67 }
运行结果截图:

问题1:
自赋值问题,先删除ptr会导致vi.ptr成为悬空指针,后续访问vi.ptr[i]会产生未定义行为。
new int[n]内存分配失败,对象处于无效状态,破坏了异常安全保证。
原版本先分配新内存再释放旧内存,确保操作失败时原数据不受影响。
问题2:
(1)添加const限定符,转换前this类型vectorInt*,转换后this类型const vectorInt*,目的是将当前对象的指针强制转换为const版本。
(2)移除const限定符,转换前返回类型为const int&,转换后返回类型为int&,目的是移除const限定符,返回非const引用以支持修改操作。
问题3:
(1)auto it1=v1.begin();调用int* begin();auto it2=v2.begin();调用const int* begin() const.非const版本用于需要修改容器元素的场景,返回普通指针;const版本用于只读访问场景,返回const指针保证数据不被修改。
(2)迭代器本质是提供"遍历能力"的抽象,对于连续内存容器,原始指针已具备完整的迭代器功能,复杂容器需要专门类来封装遍历逻辑。
问题4:
可以。std::fill_n(ptr, n, value);功能是从ptr指向的数组开始,连续填充n个元素,每个元素的值为value.
std::copy_n(vi.ptr, vi.n, ptr);功能是从vi.ptr指向的数组开始,复制vi.n个元素到ptr指向的目标数组.
1 #pragma once 2 3 class Matrix{ 4 public: 5 Matrix(int rows_,int cols_,double value=0); 6 Matrix(int rows_,double value=0); 7 Matrix(const Matrix &x); 8 ~Matrix(); 9 10 void set(const double *pvalue,int size); 11 void clear(); 12 13 const double& at(int i,int j) const; 14 double& at(int i,int j); 15 16 int rows() const; 17 int cols() const; 18 19 void print() const; 20 21 private: 22 int n_rows; 23 int n_cols; 24 double *ptr; 25 };
源代码matrix.cpp
1 #include "matrix.hpp" 2 #include<iostream> 3 #include<iomanip> 4 5 Matrix::Matrix(int rows_,int cols_,double value):n_rows(rows_),n_cols(cols_){ 6 int size=n_rows*n_cols; 7 ptr=new double[size]; 8 for(int i=0;i<size;++i){ 9 ptr[i]=value; 10 } 11 } 12 13 Matrix::Matrix(int rows_,double value):Matrix(rows_,rows_,value){ 14 } 15 16 Matrix::Matrix(const Matrix &x):n_rows(x.n_rows),n_cols(x.n_cols){ 17 int size=n_rows*n_cols; 18 ptr=new double[size]; 19 for(int i=0;i<size;i++){ 20 ptr[i]=x.ptr[i]; 21 } 22 } 23 24 Matrix::~Matrix(){ 25 delete[] ptr; 26 } 27 28 void Matrix::set(const double *pvalue,int size){ 29 if(size!=n_rows*n_cols){ 30 std::cerr << "Error:Size mismatch in set function." << std::endl; 31 exit(1); 32 } 33 for(int i=0;i<size;i++){ 34 ptr[i]=pvalue[i]; 35 } 36 } 37 38 void Matrix::clear(){ 39 int size=n_rows*n_cols; 40 for(int i=0;i<size;i++){ 41 ptr[i]=0.0; 42 } 43 } 44 45 const double& Matrix::at(int i,int j) const{ 46 if(i<0||i>=n_rows||j<0||j>=n_cols){ 47 std::cerr << "IndexError: Index out of range." << std::endl; 48 exit(1); 49 } 50 return ptr[i*n_cols+j]; 51 } 52 53 double& Matrix::at(int i,int j){ 54 return const_cast<double&>(static_cast<const Matrix*>(this)->at(i,j)); 55 } 56 57 int Matrix::rows() const{ 58 return n_rows; 59 } 60 61 int Matrix::cols() const{ 62 return n_cols; 63 } 64 65 void Matrix::print() const { 66 for(int i=0;i<n_rows;++i){ 67 std::cout << at(i,0); 68 for(int j=1;j<n_cols;j++){ 69 std::cout << "," << at(i,j); 70 } 71 std::cout << std::endl; 72 } 73 }
源代码task4.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 13 std::cout << "\n测试2: \n"; 14 test2(); 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 27 Matrix m2(m, n); 28 m2.set(x, m*n); 29 30 Matrix m3(n); 31 m3.set(x, n*n); 32 33 std::cout << "矩阵对象m1: \n"; m1.print(); 34 std::cout << "矩阵对象m2: \n"; m2.print(); 35 std::cout << "矩阵对象m3: \n"; m3.print(); 36 } 37 38 void test2() { 39 Matrix m1(2, 3, -1); 40 const Matrix m2(m1); 41 42 std::cout << "矩阵对象m1: \n"; m1.print(); 43 std::cout << "矩阵对象m2: \n"; m2.print(); 44 45 m1.clear(); 46 m1.at(0, 0) = 1; 47 48 std::cout << "m1更新后: \n"; 49 std::cout << "矩阵对象m1第0行 "; output(m1, 0); 50 std::cout << "矩阵对象m2第0行: "; output(m2, 0); 51 } 52 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 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 }
运行结果截图:

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

实验总结:
本次实验使用C++标准库容器std::vector和算法std::sort,显著减少了代码量并提升了安全性。用std::sort实现字典序排序,代码简洁且高效,比手动实现冒泡排序、选择排序更方便,这让我认识到C++标准库在代码实现中的便利。在代码实现时要注意细节问题,防止因字母敲错,语句漏掉引起运行不成功。

浙公网安备 33010602011771号