OOP-实验3
实验任务1
源代码task1
1 // Button类定义、实现 2 3 #pragma once 4 5 #include <iostream> 6 #include <string> 7 8 class Button 9 { 10 public: 11 Button(const std::string &label_); 12 const std::string &get_label() const; 13 void click(); 14 15 private: 16 std::string label; 17 }; 18 19 Button::Button(const std::string &label_) : label{label_} {} 20 21 inline const std::string &Button::get_label() const 22 { 23 return label; 24 } 25 26 void Button::click() 27 { 28 std::cout << "Button '" << label << "' clicked." << std::endl; 29 }
1 // Window类定义、实现 2 3 #pragma once 4 5 #include <iostream> 6 #include <vector> 7 #include <algorithm> 8 #include "button.hpp" 9 10 // 窗口类 11 class Window 12 { 13 public: 14 Window(const std::string &title_); 15 16 void display() const; 17 void close(); 18 void add_button(const std::string &label); 19 void click_button(const std::string &label); 20 21 private: 22 bool has_button(const std::string &label) const; 23 24 private: 25 std::string title; 26 std::vector<Button> buttons; 27 }; 28 29 Window::Window(const std::string &title_) : title{title_} 30 { 31 buttons.push_back(Button("close")); // 初始化添加close按钮 32 } 33 34 inline void Window::display() const 35 { 36 std::string s(40, '*'); 37 std::cout << s << std::endl; 38 39 std::cout << "window : " << title << std::endl; 40 41 int cnt = 0; 42 43 for (const auto &button : buttons) 44 std::cout << ++cnt << ". " << button.get_label() << std::endl; 45 46 std::cout << s << std::endl; 47 } 48 49 void Window::close() 50 { 51 std::cout << "close window '" << title << "'" << std::endl; 52 click_button("close"); 53 } 54 55 // 检查是否有重复按钮 56 inline bool Window::has_button(const std::string &label) const 57 { 58 for (const auto &button : buttons) 59 if (button.get_label() == label) 60 return true; 61 62 return false; 63 } 64 65 inline void Window::add_button(const std::string &label) 66 { 67 if (has_button(label)) 68 std::cout << "button " << label << " already exists!" << std::endl; 69 else 70 buttons.push_back(Button(label)); 71 } 72 73 inline void Window::click_button(const std::string &label) 74 { 75 for (auto &button : buttons) 76 { 77 if (button.get_label() == label) 78 { 79 button.click(); 80 return; 81 } 82 } 83 84 std::cout << "no button: " << label << std::endl; 85 }
1 // 组合类的定义和使用 2 3 #include "button.hpp" 4 #include "window.hpp" 5 6 void test() 7 { 8 Window w("Demo"); 9 w.add_button("add"); 10 w.add_button("remove"); 11 w.add_button("modify"); 12 w.add_button("find"); 13 w.display(); 14 w.close(); 15 } 16 17 int main() 18 { 19 std::cout << "用组合类模拟简单GUI: " << std::endl; 20 test(); 21 }
运行测试截图

回答问题
问题1:这个范例中,Window 和 Button 是组合关系吗?
Window 和 Button 是组合关系
问题2:bool has_button(const std::string &label) const; 被设计为私有。思考并回答:
(1)若将其改为公有接口,有何优点或风险?
优点:类外部可以直接考察某个按钮是否重复存在,不必只有在添加该按钮时才能知晓它是否重复
风险:暴露了外部不必了解的按钮重复检查机制,破坏了类的封装性
(2)设计类时,如何判断一个成员函数应为 public 还是 private ?(可从“用户是否需要”、“是否仅为内部实现细节”、是否易破坏对象状态等角度分析)
若用户需要直接调用该成员函数实现核心需求,应将其设置为 public
若该成员函数为边界检查、报错提示等辅助函数,应将其设置为 private
若该成员函数易破坏对象状态,应将其设置为 private
问题3:Button 的接口 const std::string& get_label() const; 返回 const std::string& 。对比以下两种接口设计在性能和安全性方面的差异并精炼陈述。
接口1:const std::string& get_label() const;
接口2:const std::string get_label() const;
接口1:返回的是 const 引用类型,不会产生临时对象,不需要进行字符串的复制操作,因此性能更好
接口2:返回的不是引用类型,每次返回都将产生临时对象,需要进行字符串的复制,性能下降;但与原来的字符串存储空间完全分离,因此安全性更高
问题4:把代码中所有 xx.push_back(Button(xxx)) 改成 xx.emplace_back(xxx) ,观察程序是否正常运行;查阅资料,回答两种写法的差别。
emplace_back(xxx) 直接在容器内的尾部构造一个新的元素,而不需要创建临时对象
push_back(xxx) 先创建临时对象,再将该对象复制到容器尾部
emplace_back(xxx) 性能更佳且写法更为简洁
实验任务2
源代码task2
1 // 用标准库模板类vector观察、理解深复制 2 3 #include <iostream> 4 #include <vector> 5 6 void test1(); 7 void test2(); 8 void output1(const std::vector<int> &v); 9 void output2(const std::vector<int> &v); 10 void output3(const std::vector<std::vector<int>> &v); 11 12 int main() 13 { 14 std::cout << "深复制验证1: 标准库vector<int>" << std::endl; 15 test1(); 16 17 std::cout << std::endl; 18 19 std::cout << "深复制验证2: 标准库vector<int>嵌套使用" << std::endl; 20 test2(); 21 } 22 23 void test1() 24 { 25 std::vector<int> v1(5, 42); 26 const std::vector<int> v2(v1); 27 28 std::cout << "**********拷贝构造后**********" << std::endl; 29 std::cout << "v1: "; 30 output1(v1); 31 std::cout << "v2: "; 32 output1(v2); 33 34 v1.at(0) = -1; 35 36 std::cout << "**********修改v1[0]后**********" << std::endl; 37 std::cout << "v1: "; 38 output1(v1); 39 std::cout << "v2: "; 40 output1(v2); 41 } 42 43 void test2() 44 { 45 std::vector<std::vector<int>> v1{{1, 2, 3}, {4, 5, 6, 7}}; 46 const std::vector<std::vector<int>> v2(v1); 47 48 std::cout << "**********拷贝构造后**********" << std::endl; 49 std::cout << "v1: "; 50 output3(v1); 51 std::cout << "v2: "; 52 output3(v2); 53 54 v1.at(0).push_back(-1); 55 56 std::cout << "**********修改v1[0]后**********" << std::endl; 57 std::cout << "v1: "; 58 output3(v1); 59 std::cout << "v2: "; 60 output3(v2); 61 } 62 63 // 使用xx.at()+循环输出vector<int>数据项 64 void output1(const std::vector<int> &v) 65 { 66 if (v.size() == 0) 67 { 68 std::cout << std::endl; 69 return; 70 } 71 72 std::cout << v.at(0); 73 for (auto i = 1; i < v.size(); i++) 74 std::cout << ", " << v.at(i); 75 std::cout << std::endl; 76 } 77 78 // 使用迭代器+循环输出vector<int>数据项 79 void output2(const std::vector<int> &v) 80 { 81 if (v.size() == 0) 82 { 83 std::cout << std::endl; 84 return; 85 } 86 87 auto it = v.begin(); 88 89 std::cout << *it; 90 91 for (it = v.begin() + 1; it != v.end(); it++) 92 std::cout << ", " << *it; 93 std::cout << std::endl; 94 } 95 96 // 使用auto for分行输出vector<vector<int>>数据项 97 void output3(const std::vector<std::vector<int>> &v) 98 { 99 if (v.size() == 0) 100 { 101 std::cout << std::endl; 102 return; 103 } 104 105 for (auto &i : v) 106 output2(i); 107 }
运行测试截图

回答问题
问题1:测试模块1中这两行代码分别完成了什么构造?v1、v2 各包含多少个值为42的数据项?
普通构造;复制构造
5
问题2:测试模块2中这两行代码执行后,v1.size()、v2.size()、v1[0].size() 分别是多少?
v1.size() = 2
v2.size() = 2
v1[0].size() = 3
问题3:测试模块1中,把 v1.at(0) = -1; 写成 v1[0] = -1; 能否实现同等效果?两种用法有何区别?
能实现相同效果
v1.at(0) = -1 at() 会自带边界检查,若越界会有异常提示,因此更加安全
v1[0] = -1 不会自带边界检查,需要开发者手动确保索引不越界
问题4:测试模块2中执行 v1.at(0).push_back(-1); 后
(1)用以下两行代码,能否输出-1?为什么?
能够输出-1
原因:引用类型 r 绑定了 v1.at(0),即 vector<int> 容器对象 {1, 2, 3, -1}
输出 r.at(r.size() - 1) 即输出最后一个元素,所以可以输出-1
(2)r 定义成用 const & 类型接收返回值,在内存使用上有何优势?有何限制?
优势:不会调用复制构造函数,不需要创建 vector<int> 临时对象,性能更佳
限制:const 引用使得当我们使用 r 来访问时,不能修改它的值
问题5:观察程序运行结果,反向分析、推断:
(1)标准库模板类 vector 的复制构造函数实现的是深复制还是浅复制?
深复制
(2)vector<T>::at() 接口思考:当 v 是 vector<int> 时,v.at(0) 返回值类型是什么?当 v 是 const vector<int> 时,v.at(0) 返回值类型又是什么?据此推断 at() 是否必须提供带 const 修饰的重载版本?
vector<int> 返回 int&
const vector<int> 返回 const int&
因此 at() 必须提供带 const 修饰的重载版本
实验任务3
源代码task3
1 #pragma once 2 3 #include <iostream> 4 5 // 动态int数组对象类 6 class vectorInt 7 { 8 public: 9 vectorInt(); 10 vectorInt(int n_); 11 vectorInt(int n_, int value); 12 vectorInt(const vectorInt &vi); 13 ~vectorInt(); 14 15 int size() const; 16 int &at(int index); 17 const int &at(int index) const; 18 vectorInt &assign(const vectorInt &vi); 19 20 int *begin(); 21 int *end(); 22 const int *begin() const; 23 const int *end() const; 24 25 private: 26 int n; // 当前数据项个数 27 int *ptr; // 数据区 28 }; 29 30 vectorInt::vectorInt() : n{0}, ptr{nullptr} {} 31 32 vectorInt::vectorInt(int n_) : n{n_}, ptr{new int[n]} {} 33 34 vectorInt::vectorInt(int n_, int value) : n{n_}, ptr{new int[n]} 35 { 36 for (auto i = 0; i < n; i++) 37 ptr[i] = value; 38 } 39 40 // 深复制 41 vectorInt::vectorInt(const vectorInt &vi) : n{vi.n}, ptr{new int[n]} 42 { 43 for (auto i = 0; i < n; i++) 44 ptr[i] = vi.ptr[i]; 45 } 46 47 vectorInt::~vectorInt() 48 { 49 delete[] ptr; 50 } 51 52 inline int vectorInt::size() const 53 { 54 return n; 55 } 56 57 const int &vectorInt::at(int index) const 58 { 59 // 边界检查 60 if (index < 0 || index >= n) 61 { 62 std::cerr << "IndexError: index out of range" << std::endl; 63 std::exit(1); 64 } 65 66 return ptr[index]; 67 } 68 69 int &vectorInt::at(int index) 70 { 71 if (index < 0 || index >= n) 72 { 73 std::cerr << "IndexError: index out of range" << std::endl; 74 std::exit(1); 75 } 76 77 return ptr[index]; 78 } 79 80 vectorInt &vectorInt::assign(const vectorInt &vi) 81 { 82 if (this == &vi) 83 return *this; 84 85 // 深复制 86 int *ptr_tmp; 87 ptr_tmp = new int[vi.n]; 88 for (int i = 0; i < vi.n; i++) 89 ptr_tmp[i] = vi.ptr[i]; 90 91 delete[] ptr; 92 93 n = vi.n; 94 ptr = ptr_tmp; 95 return *this; 96 } 97 98 int *vectorInt::begin() 99 { 100 return ptr; 101 } 102 103 int *vectorInt::end() 104 { 105 return ptr + n; 106 } 107 108 const int *vectorInt::begin() const 109 { 110 return ptr; 111 } 112 113 const int *vectorInt::end() const 114 { 115 return ptr + n; 116 }
1 // 不使用标准库vector,自定义简化版vectorInt类,深度理解深复制 2 3 #include <iostream> 4 #include "vectorInt.hpp" 5 6 void test1(); 7 void test2(); 8 void output1(const vectorInt &vi); 9 void output2(const vectorInt &vi); 10 11 int main() 12 { 13 std::cout << "测试1: " << std::endl; 14 test1(); 15 16 std::cout << std::endl; 17 18 std::cout << "测试2: " << std::endl; 19 test2(); 20 } 21 22 void test1() 23 { 24 int n; 25 std::cout << "Enter n: "; 26 std::cin >> n; 27 28 vectorInt x1(n); 29 for (auto i = 0; i < n; i++) 30 x1.at(i) = (i + 1) * 10; 31 std::cout << "x1: "; 32 output1(x1); 33 34 vectorInt x2(n, 42); 35 vectorInt x3(x2); 36 x2.at(0) = -1; 37 std::cout << "x2: "; 38 output1(x2); 39 std::cout << "x3: "; 40 output1(x3); 41 } 42 43 void test2() 44 { 45 const vectorInt x(5, 42); 46 vectorInt y; 47 48 y.assign(x); 49 50 std::cout << "x: "; 51 output2(x); 52 std::cout << "y: "; 53 output2(y); 54 } 55 56 // 使用xx.at()+循环输出vectorInt对象数据项 57 void output1(const vectorInt &vi) 58 { 59 if (vi.size() == 0) 60 { 61 std::cout << std::endl; 62 return; 63 } 64 65 std::cout << vi.at(0); 66 for (auto i = 1; i < vi.size(); i++) 67 std::cout << ", " << vi.at(i); 68 std::cout << std::endl; 69 } 70 71 void output2(const vectorInt &vi) 72 { 73 if (vi.size() == 0) 74 { 75 std::cout << std::endl; 76 return; 77 } 78 79 auto it = vi.begin(); 80 std::cout << *it; 81 82 for (auto it = vi.begin() + 1; it != vi.end(); it++) 83 std::cout << ", " << *it; 84 std::cout << std::endl; 85 }
运行测试截图

回答问题
问题1:当前验证性代码中,vectorInt 接口 assign 实现是安全版本。如果把 assign 实现改成版本2,逐条指出版本2存在的安全隐患和缺陷。(提示: 对比两个版本,找出差异化代码,加以分析)
原来的 assign 实现是 对自我赋值的处理 -> 分配新内存 -> 将数据复制到新内存中 -> 释放旧内存
版本2的实现是 释放旧内存 -> 分配新内存 -> 数据复制
版本2并未考虑自我赋值的情况,可能导致立即删除指向自己存储空间的 ptr ,且先释放原有资源再分配新资源的行为也存在安全隐患
问题2:当前验证性代码中,重载接口 at 内部代码完全相同。若把非 const 版本改成如下实现,可消除重复并遵循“最小化接口”原则(未来如需更新接口,只更新 const 接口,另一个会同步)。
(1)static_cast<const vectorInt*>(this) 的作用是什么?转换前后 this 的类型分别是什么?转换目的?
作用及类型:将 this 指针类型从 vectorInt* 转换为 const vectorInt*
转换目的:可以调用 const int& at(int index) const;
(2)const_cast<int&> 的作用是什么?转换前后的返回类型分别是什么?转换目的?
作用及类型:将 const int& 转换为 int&
转换目的:非 const 版本转换后依然为非 const,但实现过程中可以直接调用 const 版本的接口,因此更新接口时只需更新 const 接口即可,该非 const 接口也会同步更新
问题3:vectorInt 类封装了 begin() 和 end() 的 const /非 const 接口。
(1)以下代码片段,分析编译器如何选择重载版本,并总结这两种重载分别适配什么使用场景。
auto it1 = v1.begin(); 调用非 const 的 int* begin();
auto it2 = v2.begin(); 调用 const int* begin() const;
当需要修改容器内元素的值时,使用非 const 的迭代器指针;
当容器内元素不可修改、只可读取时,使用 const 迭代器指针
(2)标准库迭代器本质上是指针的封装。vectorInt 直接返回原始指针作为迭代器,这种设计让你对迭代器有什么新的理解?
迭代器的设计目的即为遍历访问容器中的元素,原始指针恰好可以满足这种需求
问题4:以下两个构造函数及 assign 接口实现,都包含内存块的赋值和复制操作。使用算法库 <algorithm> 改成如下写法是否可以?回答这3行更新代码的功能。
可以;
std::fill_n(ptr, n, value); 用特定值 value 赋值指定数量 n 个元素到 ptr 所指向的连续空间中
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 所指向的空间中
实验任务4
源代码task4
1 #pragma once 2 3 #include <iostream> 4 5 // 类Matrix声明 6 class Matrix 7 { 8 public: 9 Matrix(int rows_, int cols_, double value = 0); // 构造rows_*cols_矩阵对象,初值value 10 Matrix(int rows_, double value = 0); // 构造rows_*rows_方阵对象,初值value 11 Matrix(const Matrix &x); // 深复制 12 ~Matrix(); 13 14 void set(const double *pvalue, int size); // 按行复制pvalue指向的数据,要求size=rows*cols,否则报错退出 15 void clear(); // 矩阵对象数据项置0 16 17 const double &at(int i, int j) const; // 返回矩阵对象索引(i, j)对应的数据项const引用(越界则报错后退出) 18 double &at(int i, int j); // 返回矩阵对象索引(i, j)对应的数据项引用(越界则报错后退出) 19 20 int rows() const; // 返回矩阵对象行数 21 int cols() const; // 返回矩阵对象列数 22 23 void print() const; // 按行打印数据 24 25 private: 26 int n_rows; // 矩阵对象内元素行数 27 int n_cols; // 矩阵对象内元素列数 28 double *ptr; // 数据区 29 };
1 // Matrix类的实现 2 3 #include "matrix.hpp" 4 5 Matrix::Matrix(int rows_, int cols_, double value) : n_rows{rows_}, n_cols{cols_}, ptr{new double[n_rows * n_cols]} 6 { 7 for (int i = 0; i < n_rows * n_cols; i++) 8 *(ptr + i) = value; 9 } 10 11 Matrix::Matrix(int rows_, double value) : n_rows{rows_}, n_cols{rows_}, ptr{new double[n_rows * n_cols]} 12 { 13 for (int i = 0; i < n_rows * n_cols; i++) 14 *(ptr + i) = value; 15 } 16 17 Matrix::Matrix(const Matrix &x) : n_rows{x.rows()}, n_cols{x.cols()}, ptr{new double[n_rows * n_cols]} 18 { 19 for (int i = 0; i < n_rows; i++) 20 { 21 for (int j = 0; j < n_cols; j++) 22 ptr[i * n_cols + j] = x.at(i, j); 23 } 24 } 25 26 Matrix::~Matrix() 27 { 28 delete[] ptr; 29 } 30 31 void Matrix::set(const double *pvalue, int size) 32 { 33 if (n_rows * n_cols != size) 34 std::exit(1); 35 36 for (int i = 0; i < size; i++) 37 ptr[i] = pvalue[i]; 38 } 39 40 void Matrix::clear() 41 { 42 for (int i = 0; i < n_rows; i++) 43 { 44 for (int j = 0; j < n_cols; j++) 45 this->at(i, j) = 0; 46 } 47 } 48 49 const double &Matrix::at(int i, int j) const 50 { 51 if (i < 0 || i >= n_rows || j < 0 || j >= n_cols) 52 { 53 std::cerr << "IndexError: index out of range" << std::endl; 54 std::exit(1); 55 } 56 57 return *(ptr + i * n_cols + j); 58 } 59 60 double &Matrix::at(int i, int j) 61 { 62 return const_cast<double &>(static_cast<const Matrix *>(this)->at(i, j)); 63 } 64 65 inline int Matrix::rows() const 66 { 67 return n_rows; 68 } 69 70 inline int Matrix::cols() const 71 { 72 return n_cols; 73 } 74 75 void Matrix::print() const 76 { 77 std::cout << std::endl; 78 79 for (int i = 0; i < n_rows; i++) 80 { 81 std::cout << this->at(i, 0); 82 83 for (int j = 1; j < n_cols; j++) 84 std::cout << ", " << this->at(i, j); 85 86 std::cout << std::endl; 87 } 88 }
1 // 用原始指针实现动态矩阵类Matrix 2 3 #include <iostream> 4 #include <cstdlib> 5 #include "matrix.hpp" 6 #include "matrix.cpp" 7 8 void test1(); 9 void test2(); 10 void output(const Matrix &m, int row_index); 11 12 int main() 13 { 14 std::cout << "测试1: " << std::endl; 15 test1(); 16 17 std::cout << std::endl; 18 19 std::cout << "测试2: " << std::endl; 20 test2(); 21 } 22 23 void test1() 24 { 25 double x[1000] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 26 27 int n, m; 28 std::cout << "Enter n and m: "; 29 std::cin >> n >> m; 30 31 Matrix m1(n, m); // 创建矩阵对象m1,大小n*m 32 m1.set(x, n * m); // 用一维数组x的值按行为矩阵m1赋值 33 34 Matrix m2(m, n); // 创建矩阵对象m2,大小m*n 35 m2.set(x, m * n); // 用一维数组x的值按行为矩阵m2赋值 36 37 Matrix m3(n); // 创建一个n*n方阵对象 38 m3.set(x, n * n); // 用一维数组x的值按行为矩阵m3赋值 39 40 std::cout << "矩阵对象m1: "; 41 m1.print(); 42 43 std::cout << "矩阵对象m2: "; 44 m2.print(); 45 46 std::cout << "矩阵对象m3: "; 47 m3.print(); 48 } 49 50 void test2() 51 { 52 Matrix m1(2, 3, -1); 53 const Matrix m2(m1); 54 55 std::cout << "矩阵对象m1: "; 56 m1.print(); 57 58 std::cout << "矩阵对象m2: "; 59 m2.print(); 60 61 m1.clear(); 62 m1.at(0, 0) = 1; 63 64 std::cout << "m1更新后: " << std::endl; 65 66 std::cout << "矩阵对象m1第0行: "; 67 output(m1, 0); 68 69 std::cout << "矩阵对象m2第0行: "; 70 output(m2, 0); 71 } 72 73 void output(const Matrix &m, int row_index) 74 { 75 if (row_index < 0 || row_index > m.rows()) 76 { 77 std::cerr << "IndexError: row index out of range" << std::endl; 78 std::exit(1); 79 } 80 81 std::cout << m.at(row_index, 0); 82 for (int j = 1; j < m.cols(); j++) 83 std::cout << ", " << m.at(row_index, j); 84 std::cout << std::endl; 85 }
运行测试截图
实验任务5
源代码task5
1 #pragma once 2 3 #include <iostream> 4 #include <string> 5 6 // 联系人类 7 class Contact 8 { 9 public: 10 Contact(const std::string &name_, const std::string &phone_); 11 12 const std::string &get_name() const; 13 const std::string &get_phone() const; 14 15 void display() const; 16 17 private: 18 std::string name; // 必填项 19 std::string phone; // 必填项 20 }; 21 22 Contact::Contact(const std::string &name_, const std::string &phone_) : name{name_}, phone{phone_} {} 23 24 const std::string &Contact::get_name() const 25 { 26 return name; 27 } 28 29 const std::string &Contact::get_phone() const 30 { 31 return phone; 32 } 33 34 void Contact::display() const 35 { 36 std::cout << name << ", " << phone; 37 }
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 { 12 public: 13 void add(const std::string &name, const std::string &phone); // 添加联系人 14 void remove(const std::string &name); // 移除联系人 15 void find(const std::string &name) const; // 查找联系人 16 void display() const; // 显示所有联系人 17 18 size_t size() const; 19 20 private: 21 int index(const std::string &name) const; // 返回联系人在contacts内索引,如不存在,返回-1 22 void sort(); // 按姓名字典序升序排序通讯录 23 static bool compare(const Contact &a, const Contact &b); 24 25 private: 26 std::vector<Contact> contacts; 27 }; 28 29 void ContactBook::add(const std::string &name, const std::string &phone) 30 { 31 if (index(name) == -1) 32 { 33 contacts.push_back(Contact(name, phone)); 34 std::cout << name << " add successfully." << std::endl; 35 sort(); 36 return; 37 } 38 39 std::cout << name << " already exists, fail to add!" << std::endl; 40 } 41 42 void ContactBook::remove(const std::string &name) 43 { 44 int i = index(name); 45 46 if (i == -1) 47 { 48 std::cout << name << " not found, fail to remove." << std::endl; 49 return; 50 } 51 52 contacts.erase(contacts.begin() + i); 53 std::cout << name << " remove successfully." << std::endl; 54 } 55 56 void ContactBook::find(const std::string &name) const 57 { 58 int i = index(name); 59 60 if (i == -1) 61 { 62 std::cout << name << " not found!" << std::endl; 63 return; 64 } 65 66 contacts[i].display(); 67 std::cout << std::endl; 68 } 69 70 void ContactBook::display() const 71 { 72 for (auto &c : contacts) 73 { 74 c.display(); 75 std::cout << std::endl; 76 } 77 } 78 79 size_t ContactBook::size() const 80 { 81 return contacts.size(); 82 } 83 84 // 返回联系人在contacts内索引;若不存在,返回-1 85 int ContactBook::index(const std::string &name) const 86 { 87 int k = 0; 88 89 for (auto &c : contacts) 90 { 91 if (c.get_name() == name) 92 return k; 93 k++; 94 } 95 96 return -1; 97 } 98 99 bool ContactBook::compare(const Contact &a, const Contact &b) 100 { 101 if (a.get_name() < b.get_name()) 102 return true; 103 return false; 104 } 105 106 // 按姓名字典序升序排序通讯录 107 void ContactBook::sort() 108 { 109 std::sort(contacts.begin(), contacts.end(), compare); 110 }
1 // 综合使用类的组合、自定义类、C++标准库实现简化版通讯录,实现联系人增/删/查/显操作 2 3 #include "contactBook.hpp" 4 5 void test() 6 { 7 ContactBook contactbook; 8 9 std::cout << "1. add contacts" << std::endl; 10 contactbook.add("Bob", "18199357253"); 11 contactbook.add("Alice", "17300886371"); 12 contactbook.add("Linda", "18184538072"); 13 contactbook.add("Alice", "17300886371"); 14 15 std::cout << std::endl; 16 17 std::cout << "2. display contacts" << std::endl; 18 std::cout << "There are " << contactbook.size() << " contacts." << std::endl; 19 contactbook.display(); 20 21 std::cout << std::endl; 22 23 std::cout << "3. find contacts" << std::endl; 24 contactbook.find("Bob"); 25 contactbook.find("David"); 26 27 std::cout << std::endl; 28 29 std::cout << "4. remove contact" << std::endl; 30 contactbook.remove("Bob"); 31 contactbook.remove("David"); 32 } 33 34 int main() 35 { 36 test(); 37 }
运行测试截图

实验总结
1. emplace_back() 直接在 vector 末尾“原地”构造新元素;
2. vector 容器的 erase() 函数可以从容器中移除一个或多个元素,返回指向被移除元素的下一个元素的指针;若移除了最后一个元素,则返回 end();
3. size_t 表示无符号整数类型,专门用于表示对象大小、数组索引和容器元素数量
4. std::sort(iterator begin, iterator end, _Pred比较函数)
非静态成员函数不能直接用作 std::sort 的比较函数,因为该函数有一个隐式的 this 指针;
解决方法:静态成员函数/普通函数


浙公网安备 33010602011771号