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 }
button.hpp
 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 }
window.hpp
 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 }
task1.cpp

运行测试截图

task1

回答问题

问题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 }
task2.cpp

运行测试截图

task2

回答问题

问题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 }
vectorInt.hpp
 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 }
task3.cpp

运行测试截图

task3

回答问题

问题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 };
matrix.hpp
 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 }
matrix.cpp
 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 }
task4.cpp

运行测试截图

task4

 

实验任务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 }
contact.hpp
  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 }
contactBook.hpp
 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 }
task5.cpp

运行测试截图

task5

 

实验总结

1. emplace_back()  直接在 vector 末尾“原地”构造新元素;

2. vector 容器的 erase() 函数可以从容器中移除一个或多个元素,返回指向被移除元素的下一个元素的指针;若移除了最后一个元素,则返回 end();

3. size_t 表示无符号整数类型,专门用于表示对象大小、数组索引和容器元素数量

4. std::sort(iterator begin, iterator end, _Pred比较函数)

 非静态成员函数不能直接用作 std::sort 的比较函数,因为该函数有一个隐式的 this 指针;

 解决方法:静态成员函数/普通函数

posted @ 2025-11-22 20:55  FF-10086  阅读(0)  评论(0)    收藏  举报