实验3
一、实验任务1
源代码button.hpp
1 #pragma once 2 #include<iostream> 3 #include<string> 4 class Button{ 5 public: 6 Button(const std::string &label_); 7 const std::string& get_label() const; 8 void click(); 9 private: 10 std::string label; 11 }; 12 Button::Button(const std::string &label_):label{label_}{} 13 inline const std::string& Button::get_label() const{ 14 return label; 15 } 16 inline void Button::click(){ 17 std::cout<<"Button '"<<label<<"' clicked\n"; 18 }
源代码window.hpp
1 #pragma once 2 #include<iostream> 3 #include<vector> 4 #include<algorithm> 5 #include "button.hpp" 6 class window{ 7 public: 8 window(const std::string &title_); 9 void display() const; 10 void close(); 11 void add_button(const std::string &label); 12 void click_button(const std::string &label); 13 private: 14 bool has_button(const std::string &label) const; 15 private: 16 std::string title; 17 std::vector<Button> buttons; 18 }; 19 window::window(const std::string &title_):title{title_}{ 20 buttons.push_back(Button("close")); 21 } 22 inline void window::display() const{ 23 std::string s(40,'*'); 24 std::cout<<s<<std::endl; 25 std::cout<<"window:"<<title<<std::endl; 26 int cnt=0; 27 for(const auto &button:buttons) 28 std::cout<<++cnt<<"."<<button.get_label()<<std::endl; 29 std::cout<<s<<std::endl; 30 } 31 inline void window::close(){ 32 std::cout<<"close window '"<<title<<"'"<<std::endl; 33 click_button("close"); 34 } 35 inline bool window::has_button(const std::string &label)const{ 36 for(const auto &button:buttons) 37 if(button.get_label()==label) 38 return true; 39 return false; 40 } 41 inline void window::add_button(const std::string &label){ 42 if(has_button(label)) 43 std::cout<<"button "<<label<<"already exists!\n"; 44 else 45 buttons.push_back(Button(label)); 46 } 47 inline void window::click_button(const std::string &label){ 48 for(auto &button:buttons) 49 if(button.get_label()==label){ 50 button.click(); 51 return; 52 } 53 std::cout<<"no button:"<<label<<std::endl; 54 }
源代码task1.cpp
1 #include "window.hpp" 2 #include <iostream> 3 void test(){ 4 window w("Demo"); 5 w.add_button("add"); 6 w.add_button("remove"); 7 w.add_button("modify"); 8 w.add_button("add"); 9 w.display(); 10 w.close(); 11 } 12 int main(){ 13 std::cout<<"用组合类模拟简单GUI:\n"; 14 test(); 15 }
运行结果展示:

ps:在给出的示例代码中window出现了w大小写不统一的情况,需要稍加修改成统一形式。
问题1:在这个范例中,window和Button是组合关系吗?
答:是组合关系。类的的组合是has-a的关系,指的是一个类中的成员数据包含另一个类的对象,也就是这里在window类中包含有std::vector<Button> buttons(Button类的对象)。
问题2:bool has_button(const std::string &label) const; 被设计为私有。思考并回答:
(1)若将其改为公有接口,有何优点或风险?
答:这个函数是用来判断某个按键是否存在。如果改成公有接口的话,则在类的外部也可以直接进行调用来判断按键的存在性,但同时将私有转公有接口也会暴露类的内部,一定程度上有破坏类的封装性的风险,如果外部对直接调用的需要不强烈,我觉得还是私有接口更好一些,比较安全。
(2)设计类时,如何判断一个成员函数应为public还是private?(可从“用户是否需要”、“是否仅为内部实现 细节”、“是否易破坏对象状态”等角度分析。)
答:在设计一个类时:1、如果用户需要的话,成员函数应为public类型供用户直接调用,反之定为private类型,减少对封装性的破坏。2.如果仅为内部实现细节,定为private类型就可以了,在类的内部直接完成对该函数的调用。3.如果易破坏对象状态,定为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返回值是引用类型,接口2返回值是普通的字符串类型副本。从性能上看,使用引用类型可以节省内存,返回的就是对象本身,不用拷贝一份对象的副本了,效率会更高一些。从安全性上看,一个函数不能返回局部对象的引用,当返回的是局部对象的引用(也就是在函数内部定义的对象)时,每次函数调用完都会释放一次局部对象的内存,这种情况下返回引用的地址里就没有内容了,无效返回,安全性差,所以接口2返回副本的安全性更好一些。
问题4:把代码中所有 xx.push_back(Button(xxx)) 改成 xx.emplace_back(xxx) ,观察程序是否正常运 行;查阅资料,回答两种写法的差别。
答:可以正常运行。push_back函数和emplace_back函数都可以在vector容器的末尾添加数据,但push_back函数会先构造一个临时对象,然后再将其复制到容器中,而emplace_back函数会直接在容器末尾调用构造函数,避免临时对象的创建及拷贝。
(我查的资料来源:CSDN【cpp中容器push_back和emplace_back的区别 - CSDN App】https://blog.csdn.net/qq_44940689/article/details/142970810?sharetype=blog&shareId=142970810&sharerefer=APP&sharesource=2402_88154239&sharefrom=link)
二、实验任务2
源代码task2.cpp
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 int main(){ 9 std::cout<<"深复制验证1:标准库vector<int>\n"; 10 test1(); 11 std::cout<<"\n深复制验证2:标准库vector<int>嵌套使用\n"; 12 test2(); 13 } 14 void test1(){ 15 std::vector<int> v1(5,42); 16 const std::vector<int> v2(v1); 17 std::cout<<"**********拷贝构造后**********\n"; 18 std::cout<<"v1:";output1(v1); 19 std::cout<<"v2:";output1(v2); 20 v1.at(0)=-1; 21 std::cout<<"**********修改v1[0]后**********\n"; 22 std::cout<<"v1:";output1(v1); 23 std::cout<<"v2:";output1(v2); 24 } 25 void test2(){ 26 std::vector<std::vector<int>> v1{{1,2,3},{4,5,6,7}}; 27 const std::vector<std::vector<int>> v2(v1); 28 std::cout<<"**********拷贝构造后**********\n"; 29 std::cout<<"v1:\n";output3(v1); 30 std::cout<<"v2:\n";output3(v2); 31 v1.at(0).push_back(-1); 32 std::cout<<"**********修改v1[0]后**********\n"; 33 std::cout<<"v1:\n";output3(v1); 34 std::cout<<"v2:\n";output3(v2); 35 } 36 void output1(const std::vector<int> &v){ 37 if(v.size()==0){ 38 std::cout<<'\n'; 39 return; 40 } 41 std::cout<<v.at(0); 42 for(auto i=1;i<v.size();++i) 43 std::cout<<","<<v.at(i); 44 std::cout<<'\n'; 45 } 46 void output2(const std::vector<int> &v){ 47 if(v.size()==0){ 48 std::cout<<'\n'; 49 return; 50 } 51 auto it=v.begin(); 52 std::cout<<*it; 53 for(it=v.begin()+1;it!=v.end();++it) 54 std::cout<<","<<*it; 55 std::cout<<'\n'; 56 } 57 void output3(const std::vector<std::vector<int>> &v){ 58 if(v.size()==0){ 59 std::cout<<'\n'; 60 return; 61 } 62 for(auto &i:v) 63 output2(i); 64 }
运行结果展示:

问题1:测试模块1中这两行代码分别完成了什么构造? v1 、v2各包含多少个值为42的数据项?

答:第一行代码定义了一个元素为int类型的vector容器v1,里面有5个元素,每个元素的值都为42。第二行代码定义了一个const类型的元素为int类型的vector容器v2,并且使用复制构造函数,将v1中的元素复制到v2中。v1和v2都包含5个值为42的元素。
问题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; 能否实现同等效果?两种用法有何区别?
答:效果等同。at( )和[ ]都可以用来访问容器中的元素,但at( )可以检查在访问容器时是否越界,更安全一些。
问题4:测试模块2中执行v1.at(0).push_back(-1); 后
(1)用以下两行代码,能否输出-1?为什么?

答:可以输出-1。因为r是v1.at(0)的引用类型,容器内现在有1,2,3,-1,且r.size()-1=3,所以可以输出容器内最后一个元素-1。

(2)r定义成用const&类型接收返回值,在内存使用上有何优势?有何限制?
答:在内存使用上可以节省内存避免拷贝,不需要分配新内存来复制vector容器中的所有元素。由于是const&类型,常量引用,所以不能对被绑定的对象进行修改。
问题5:观察程序运行结果,反向分析、推断:
(1)标准库模板类vector的复制构造函数实现的是深复制还是浅复制?
答:深复制,v2没有被改变。
(2)vector<T>::at()接口思考:当v是vector<int>时,v.at(0)返回值类型是什么?当v是const vector<int>时,v.at(0)返回值类型又是什么?据此推断at()是否必须提供带const修饰的重载版本?
答:当v是vector<int>时,v.at(0)返回值类型是int&;当v是const vector<int>时,v.at(0)返回值类型是const int&。at()必须提供带const修饰的重载版本,因为const对象和非const对象都可以调用const类型,这样在不同情况下此接口都能够被使用。
三、实验任务3
源代码vectorInt.hpp
1 #pragma once 2 #include<iostream> 3 class vectorInt{ 4 public: 5 vectorInt(); 6 vectorInt(int n_); 7 vectorInt(int n_,int value); 8 vectorInt(const vectorInt &vi); 9 ~vectorInt(); 10 int size() const; 11 int& at(int index); 12 const int& at(int index) const; 13 vectorInt& assign(const vectorInt &vi); 14 int* begin(); 15 int* end(); 16 const int* begin() const; 17 const int* end() const; 18 private: 19 int n; 20 int *ptr; 21 }; 22 vectorInt::vectorInt():n{0},ptr{nullptr}{} 23 vectorInt::vectorInt(int n_):n{n_},ptr{new int[n_]}{} 24 vectorInt::vectorInt(int n_,int value):n{n_},ptr{new int[n_]}{ 25 for(auto i=0;i<n;i++) 26 ptr[i]=value; 27 } 28 vectorInt::vectorInt(const vectorInt &vi):n{vi.n},ptr{new int[n]}{ 29 for(auto i=0;i<n;i++) 30 ptr[i]=vi.ptr[i]; 31 } 32 vectorInt::~vectorInt(){ 33 delete[]ptr; 34 } 35 int vectorInt::size() const{ 36 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 if(index<0||index>=n){ 47 std::cerr<<"IndexError: index out of range\n"; 48 std::exit(1); 49 } 50 return ptr[index]; 51 } 52 vectorInt& vectorInt::assign(const vectorInt &vi){ 53 if(this==&vi) 54 return *this; 55 int *ptr_tmp; 56 ptr_tmp=new int[vi.n]; 57 for(int i=0;i<vi.n;i++) 58 ptr_tmp[i]=vi.ptr[i]; 59 delete[]ptr; 60 n=vi.n; 61 ptr=ptr_tmp; 62 return *this; 63 } 64 int* vectorInt::begin(){ 65 return ptr; 66 } 67 int* vectorInt::end(){ 68 return ptr+n; 69 } 70 const int* vectorInt::begin() const{ 71 return ptr; 72 } 73 const int* vectorInt::end() const{ 74 return ptr+n; 75 }
源代码task3.cpp
1 #include"vectorInt.hpp" 2 #include<iostream> 3 void test1(); 4 void test2(); 5 void output1(const vectorInt &vi); 6 void output2(const vectorInt &vi); 7 int main(){ 8 std::cout<<"测试1: \n"; 9 test1(); 10 std::cout<<"\n测试2: \n"; 11 test2(); 12 } 13 void test1(){ 14 int n; 15 std::cout<<"Enter n:"; 16 std::cin>>n; 17 vectorInt x1(n); 18 for(auto i=0;i<n;i++) 19 x1.at(i)=(i+1)*10; 20 std::cout<<"x1:"; 21 output1(x1); 22 vectorInt x2(n,42); 23 vectorInt x3(x2); 24 x2.at(0)=-1; 25 std::cout<<"x2:";output1(x2); 26 std::cout<<"x3:";output1(x3); 27 } 28 void test2(){ 29 const vectorInt x(5,42); 30 vectorInt y; 31 y.assign(x); 32 std::cout<<"x:";output2(x); 33 std::cout<<"y:";output2(y); 34 } 35 void output1(const vectorInt &vi){ 36 if(vi.size()==0){ 37 std::cout<<'\n'; 38 return; 39 } 40 std::cout<<vi.at(0); 41 for(auto i=1;i<vi.size();i++) 42 std::cout<<","<<vi.at(i); 43 std::cout<<'\n'; 44 } 45 void output2(const vectorInt &vi){ 46 if(vi.size()==0){ 47 std::cout<<'\n'; 48 return; 49 } 50 auto it=vi.begin(); 51 std::cout<<*it; 52 for(it=vi.begin()+1;it!=vi.end();it++) 53 std::cout<<","<<*it; 54 std::cout<<'\n'; 55 }
运行结果展示:

问题1:当前验证性代码中,vectorInt接口assign实现是安全版本。如果把assign实现改成版本2,逐条指出版本2存在的安全隐患和缺陷。(提示:对比两个版本,找出差异化代码,加以分析)

答:1、版本2没有if(this==&vi)来判断是否为自赋值的操作,自赋值没有意义而且浪费内存。
2、版本2把delete[]ptr放在第一行,当没有自赋值操作时,遇到自赋值情况首先delete[]ptr会把自身释放掉,使vi.ptr[i]成为空值,后续无法对其进行访问。
3.在版本1中分配内存ptr_tmp=new int[vi.n]用的是vi.n,在版本2中分配内存用的是ptr=new int[n]。如果像版本2一样先为n赋值,当面临new还没有成功分配内存时,在后续遍历访问0到n时可能出现越界访问。
问题2:当前验证性代码中,重载接口at内部代码完全相同。若把非const版本改成如下实现,可消除重复并遵循“最小化接口”原则(未来如需更新接口,只更新const接口,另一个会同步)。

查阅资料,回答:
(1)static_cast<const vectorInt*>(this)的作用是什么?转换前后this的类型分别是什么?转换目的?
答:static_cast<const vectorInt*>(this)进行类型转换;把this指针从vectorInt*类型转换成const vectorInt*类型 (也就是从非const类型到const类型);可以增加安全性,仅一个接口就可以满足const对象和非const对象的使用,不需要再写重复率很高的两个重载接口。
(2)const_cast<int&>的作用是什么?转换前后的返回类型分别是什么?转换目的?
答: const_cast<int&>用来移除对象的const限定;从const int&类型转换到int&类型;因为这个at成员函数的返回类型是int&类型,所以在函数内return时的值也要是int&类型。而直接经过static_cast<const vectorInt*>(this)类型转换得到的是const类型,所以需要再次改const为非const满足函数要求。
问题3:vectorInt类封装了begin()和end()的const/非const接口。
(1)以下代码片段,分析编译器如何选择重载版本,并总结这两种重载分别适配什么使用场景。

答:v1是非const类型对象,编译器会选择非const接口(没有的话也可以调用const);v2是const类型对象,编译器会选择const接口(不可以调用非const)。总结:const重载版本可以被const或非const对象调用,但此时不能修改其中的元素。非const重载版本只能被非const对象调用,但此时可以修改元素的值。
(2)拓展思考(选答*):标准库迭代器本质上是指针的封装。vectorInt直接返回原始指针作为迭代器,这种设计让你对迭代器有什么新的理解?
答:迭代器能够用来遍历容器的对象,在标准库里begin和end就是两个迭代器一个指向第一个元素,另一个指向最后一个元素,这两个迭代器的作用也就是像指针一样可以定位到收尾元素,但它们写起来会更加简洁一些,不需要写指针的符号。
问题4:以下两个构造函数及assign接口实现,都包含内存块的赋值和复制操作。使用算法库 改成如下写法是否可以?回答这3行更新代码的功能。

答:可以。第一行代码的功能是让ptr指向的对象被n个值为value的元素所填充;第二行代码的功能是把vi对象中从vi.ptr开始的vi.n个元素都复制到ptr所指向的对象中;第三行代码的功能与第二行类似,把vi对象中从vi.ptr开始的vi.n个元素都复制到ptr_tmp所指向的对象中。
四、实验任务4
源代码matrix.hpp
1 #pragma once 2 class Matrix{ 3 public: 4 Matrix(int rows_,int cols_,double value=0); 5 Matrix(int rows_,double value=0); 6 Matrix(const Matrix &x); 7 ~Matrix(); 8 void set(const double *pvalue,int size); 9 void clear(); 10 const double& at(int i,int j)const; 11 double& at(int i,int j); 12 int rows() const; 13 int cols() const; 14 void print() const; 15 private: 16 int n_rows; 17 int n_cols; 18 double *ptr; 19 };
源代码matrix.cpp
1 #include<iostream> 2 #include<cstdlib> 3 #include"matrix.hpp" 4 Matrix::Matrix(int rows_,int cols_,double value):n_rows{rows_},n_cols{cols_},ptr{new double[n_rows*n_cols]}{ 5 for(auto i=0;i<n_rows*n_cols;i++) 6 ptr[i]=value; 7 } 8 Matrix::Matrix(int rows_,double value):n_rows{rows_},n_cols{rows_},ptr{new double[n_rows*n_rows]}{ 9 for(auto i=0;i<n_rows*n_rows;i++) 10 ptr[i]=value; 11 } 12 Matrix::Matrix(const Matrix &x){ 13 n_rows=x.rows(); 14 n_cols=x.cols(); 15 ptr=new double[n_rows*n_cols]; 16 for(auto i=0;i<n_rows*n_cols;i++) 17 ptr[i]=x.ptr[i]; 18 } 19 Matrix::~Matrix(){ 20 delete[]ptr; 21 } 22 void Matrix::set(const double *pvalue,int size){ 23 if(size!=n_rows*n_cols){ 24 std::cerr<<"sizeError\n"; 25 std::exit(1); 26 } 27 for(auto i=0;i<size;i++) 28 ptr[i]=pvalue[i]; 29 } 30 void Matrix::clear(){ 31 for(auto i=0;i<n_rows*n_cols;i++) 32 ptr[i]=0; 33 } 34 const double& Matrix::at(int i,int j)const{ 35 if(i<0||i>=n_rows||j<0||j>=n_cols){ 36 std::cerr<<"'i'or'j'Error\n"; 37 std::exit(1); 38 } 39 return ptr[i*n_cols+j]; 40 } 41 double& Matrix::at(int i,int j){ 42 return const_cast<double&>(static_cast<const Matrix*>(this)->at(i,j)); 43 } 44 int Matrix::rows() const{ 45 return n_rows; 46 } 47 int Matrix::cols() const{ 48 return n_cols; 49 } 50 void Matrix::print() const{ 51 for(auto i=0;i<n_rows;i++){ 52 for(auto j=0;j<n_cols-1;j++) 53 std::cout<<Matrix::at(i,j)<<","; 54 std::cout<<Matrix::at(i,n_cols-1)<<"\n"; 55 } 56 }
源代码task4.cpp
1 #include<iostream> 2 #include<cstdlib> 3 #include"matrix.hpp" 4 void test1(); 5 void test2(); 6 void output(const Matrix &m,int row_index); 7 int main() 8 { 9 std::cout<<"测试1:\n"; 10 test1(); 11 std::cout<<"\n测试2:\n"; 12 test2(); 13 } 14 void test1(){ 15 double x[1000]={1,2,3,4,5,6,7,8,9,10}; 16 int n,m; 17 std::cout<<"Enter n and m:"; 18 std::cin>>n>>m; 19 Matrix m1(n,m); 20 m1.set(x,n*m); 21 Matrix m2(m,n); 22 m2.set(x,m*n); 23 Matrix m3(n); 24 m3.set(x,n*n); 25 std::cout<<"矩阵对象m1:\n";m1.print(); 26 std::cout<<"矩阵对象m2:\n";m2.print(); 27 std::cout<<"矩阵对象m3:\n";m3.print(); 28 } 29 void test2(){ 30 Matrix m1(2,3,-1); 31 const Matrix m2(m1); 32 std::cout<<"矩阵对象m1:\n";m1.print(); 33 std::cout<<"矩阵对象m2:\n";m2.print(); 34 m1.clear(); 35 m1.at(0,0)=1; 36 std::cout<<"m1更新后:\n"; 37 std::cout<<"矩阵对象m1第0行:";output(m1,0); 38 std::cout<<"矩阵对象m2第0行:";output(m2,0); 39 } 40 void output(const Matrix &m,int row_index){ 41 if(row_index<0||row_index>m.rows()){ 42 std::cerr<<"IndexError:row index out of range\n"; 43 std::exit(1); 44 } 45 std::cout<<m.at(row_index,0); 46 for(int j=1;j<m.cols();j++) 47 std::cout<<","<<m.at(row_index,j); 48 std::cout<<'\n'; 49 }
运行成果展示:

五、实验任务5
源代码contact.hpp
1 #pragma once 2 #include<iostream> 3 #include<string> 4 class Contact{ 5 public: 6 Contact(const std::string &name_,const std::string &phone_); 7 const std::string &get_name() const; 8 const std::string &get_phone() const; 9 void display() const; 10 private: 11 std::string name; 12 std::string phone; 13 }; 14 Contact::Contact(const std::string &name_,const std::string &phone_):name{name_},phone{phone_}{} 15 const std::string& Contact::get_name() const{ 16 return name; 17 } 18 const std::string& Contact::get_phone() const{ 19 return phone; 20 } 21 void Contact::display() const{ 22 std::cout<<name<<","<<phone; 23 }
源代码contactBook.hpp
1 #pragma once 2 #include<iostream> 3 #include<string> 4 #include<vector> 5 #include<algorithm> 6 #include"contact.hpp" 7 class ContactBook{ 8 public: 9 void add(const std::string &name,const std::string &phone); 10 void remove(const std::string &name); 11 void find(const std::string &name) const; 12 void display() const; 13 size_t size() const; 14 private: 15 int index(const std::string &name) const; 16 void sort(); 17 private: 18 std::vector<Contact> contacts; 19 }; 20 void ContactBook::add(const std::string &name,const std::string &phone){ 21 if(index(name)==-1){ 22 contacts.push_back(Contact(name,phone)); 23 std::cout<<name<<" add successfully.\n"; 24 sort(); 25 return; 26 } 27 std::cout<<name<<" already exists.fail to add!\n"; 28 } 29 void ContactBook::remove(const std::string &name){ 30 int i=index(name); 31 if(i==-1){ 32 std::cout<<name<<" not found,fail to remove!\n"; 33 return; 34 } 35 contacts.erase(contacts.begin()+i); 36 std::cout<<name<<" remove successfully.\n"; 37 } 38 void ContactBook::find(const std::string &name) const{ 39 int i=index(name); 40 if(i==-1){ 41 std::cout<<name<<" not found!\n"; 42 return; 43 } 44 contacts[i].display(); 45 std::cout<<'\n'; 46 } 47 void ContactBook::display() const{ 48 for(auto &c:contacts){ 49 c.display(); 50 std::cout<<'\n'; 51 } 52 } 53 size_t ContactBook::size() const{ 54 return contacts.size(); 55 } 56 int ContactBook::index(const std::string &name) const{ 57 size_t i=0; 58 for(const auto &contact:contacts){ 59 if(name==contacts[i].get_name()) 60 return i; 61 i++; 62 } 63 return -1; 64 } 65 bool comparename(const Contact &name1,const Contact &name2){ 66 return name1.get_name()<name2.get_name(); 67 } 68 void ContactBook::sort(){ 69 std::sort(contacts.begin(),contacts.end(),comparename); 70 }
源代码task5.cpp
1 #include"ContactBook.hpp" 2 void test(){ 3 ContactBook contactbook; 4 std::cout<<"1.add contacts\n"; 5 contactbook.add("Bob","18199357253"); 6 contactbook.add("Alice","17300886371"); 7 contactbook.add("Linda","18184538072"); 8 contactbook.add("Alice","17300886371"); 9 std::cout<<"\n2.display contacts\n"; 10 std::cout<<"There are "<<contactbook.size()<<" contacts.\n"; 11 contactbook.display(); 12 std::cout<<"\n3.find contacts\n"; 13 contactbook.find("Bob"); 14 contactbook.find("David"); 15 std::cout<<"\n4.remove contact\n"; 16 contactbook.remove("Bob"); 17 contactbook.remove("David"); 18 } 19 int main(){ 20 test(); 21 }
运行成果展示:

实验总结:1.在类的成员函数声明时如果已经为某个变量赋了初值,在定义时就不需要再写一次。在完成实验4时我的函数声明和定义都写了value=0,不仔细看找不到报错点。
2.类的成员函数不能直接用做std::sort的比较函数,在实验5中,我先定义comparename是成员函数,但这种情况下需要对象来调用,不如使用自由函数简洁。
3.size_t是一种无符号整数类型,一般在表示对象大小和数组的索引里使用。


浙公网安备 33010602011771号