实验三
任务一:
button.hpp
1 #pragma once 2 #include<iostream> 3 #include<string> 4 class Button 5 { 6 public: 7 Button(const std::string &label_); 8 const std::string& get_label() const; 9 void click(); 10 private: 11 std::string label; 12 }; 13 Button::Button(const std::string &label_): label{label_} 14 { 15 } 16 inline const std::string &Button ::get_label()const 17 { 18 return label; 19 } 20 inline void Button::click() 21 { 22 std::cout<<"Button"<<label<<"clicked\n"; 23 }
window.hpp
1 #pragma once 2 #include<iostream> 3 #include<vector> 4 #include<algorithm> 5 #include "button.hpp" 6 class Window 7 { 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 private: 15 bool has_button(const std::string &label)const; 16 private: 17 std::string title; 18 std::vector<Button> buttons; 19 }; 20 Window::Window(const std::string &title_):title{title_} 21 { 22 buttons.push_back(Button("close")); 23 } 24 inline void Window::display() const 25 { 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 { 37 std::cout<<"close window'"<<title<<"'"<<std::endl; 38 click_button("close"); 39 } 40 inline bool Window::has_button(const std::string &label) const 41 { 42 for(const auto &button:buttons) 43 if(button.get_label()==label) 44 return true; 45 return false; 46 } 47 inline void Window::add_button(const std::string &label) 48 { 49 if(has_button(label)) 50 std::cout<<"button "<<label<<" already exists!\n"; 51 else 52 buttons.push_back(Button(label)); 53 } 54 inline void Window::click_button(const std::string &label) 55 { 56 for(auto &button:buttons) 57 if(button.get_label()==label) 58 { 59 button.click(); 60 return; 61 } 62 std::cout<<"no button:"<<label<<std::endl; 63 }
task1.cpp
1 #include "window.hpp" 2 #include <iostream> 3 void test() 4 { 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 int main() 14 { 15 std::cout << "用组合类模拟简单GUI:\n"; 16 test(); 17 }
运行结果截图

问题1:这个范例中,Window和Button是组合关系吗?
答:是组合关系,因为Window的对象包含Button对象作为组成部分
问题2: bool has_button(const std::string &label) const; 被设计为私有。 思考并回答:
(1)若将其改为公有接口,有何优点或风险?
答:外部代码可以检测按钮是否存在,但会破坏封装性。
(2)设计类时,如何判断一个成员函数应为 public 还是 private?(可从“用户是否需要”、“是否仅为内部实现细节”、“是否易破坏对象状态”等角度分析。)
答:看函数是否是用户调用的核心功能,接口,是的话用public,如果只是为内部实现细节或者会破坏对象完整性用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(Button(xxx))先构造一个临时对象再将临时对象复制到vector中,而emplace_back(xxx):直接在vector的中构造对象。
任务二:
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 { 10 std::cout<<"深刻复制1:标准库vector<int>\n"; 11 test1(); 12 std::cout<<"\n深刻复制2:标准库vector<int>嵌套使用\n"; 13 test2(); 14 } 15 void test1() 16 { 17 std::vector<int> v1(5,42); 18 const std::vector<int> v2(v1); 19 std::cout<<"**********拷贝构造后**********\n"; 20 std::cout<<"v1: "; 21 output1(v1); 22 std::cout<<"v2: "; 23 output1(v2); 24 v1.at(0)=-1; 25 std::cout<<"**********修改v1[0]后**********\n"; 26 std::cout<<"v1: "; 27 output1(v1); 28 std::cout<<"v2: "; 29 output1(v2); 30 } 31 void test2() 32 { 33 std::vector<std::vector<int>>v1{{1,2,3},{4,5,6,7}}; 34 const std::vector<std::vector<int>>v2(v1); 35 std::cout << "**********拷贝构造后**********\n"; 36 std::cout << "v1: "; output3(v1); 37 std::cout << "v2: "; output3(v2); 38 v1.at(0).push_back(-1); 39 std::cout << "**********修改v1[0]后**********\n"; 40 std::cout << "v1: \n"; output3(v1); 41 std::cout << "v2: \n"; output3(v2); 42 } 43 void output1(const std::vector<int> &v) 44 { 45 if(v.size()==0) 46 { 47 std::cout<<'\n'; 48 return; 49 } 50 std::cout<<v.at(0); 51 for(auto i=1;i<v.size();++i) 52 std::cout<<", "<<v.at(i); 53 std::cout<<'\n'; 54 } 55 void output2(const std::vector<int> &v) 56 { 57 if(v.size()==0) 58 { 59 std::cout<<'\n'; 60 return; 61 } 62 auto it=v.begin(); 63 std::cout<<*it; 64 for(it=v.begin()+1;it!=v.end();++it) 65 std::cout<<", "<<*it; 66 std::cout<<'\n'; 67 } 68 void output3(const std::vector<std::vector<int>> &v) 69 { 70 if(v.size()==0) 71 { 72 std::cout<<'\n'; 73 return; 74 } 75 for(auto &i:v) 76 output2(i); 77 }
运行结果截图

问题1:测试模块1中这两行代码分别完成了什么构造? v1、v2 各包含多少个值为 42 的数据项
答:v1使用了类vector的带参的构造函数,而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() 成员函数会进行边界检查,而operator[]不会。
问题4:测试模块2中执行 v1.at(0).push_back(-1); 后
(1) 用以下两行代码,能否输出-1?为什么?
答:可以。因为首先v1.at(0).push_back(-1); 作用是在v1的第一个内部量({1, 2, 3})尾部添加元素-1。
std::vector<int> &r = v1.at(0); r引用v1的第一个量,而r.at(r.size()-1) 访问该量的最后一个元素即为-1
(2)r定义成用const &类型接收返回值,在内存使用上有何优势?有何限制?
答:r用引用的方式,节省内存。但限制是r不能更改所引用的量。
问题5:观察程序运行结果,反向分析、推断:
(1) 标准库模板类vector 的复制构造函数实现的是深复制还是浅复制?
答:深复制。深复制中原量和复制量是独立的,修改其中一个不会影响另一个。实验中v1修改后有了-1,但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类型也可以调用。
任务三:
vectorInt.hpp
1 #pragma once 2 #include<iostream> 3 class vectorInt 4 { 5 public: 6 vectorInt(); 7 vectorInt(int n_); 8 vectorInt(int n_,int value); 9 vectorInt(const vectorInt &vi); 10 ~vectorInt(); 11 int size() const; 12 int& at(int index) ; 13 const int& at(int index) const; 14 vectorInt &assign(const vectorInt &vi); 15 int *begin(); 16 int *end(); 17 const int*begin() const; 18 const int *end() const; 19 private: 20 int n; 21 int *ptr; 22 }; 23 vectorInt:: vectorInt():n{0},ptr{nullptr} 24 { 25 } 26 vectorInt::vectorInt(int n_):n{n_},ptr{new int[n]} 27 { 28 } 29 vectorInt::vectorInt(int n_,int value):n{n_},ptr{new int[n_]} 30 { 31 for(auto i=0;i<n;++i) 32 ptr[i]=value; 33 } 34 vectorInt::vectorInt(const vectorInt &vi):n{vi.n},ptr{new int[n]} 35 { 36 for(auto i=0;i<n;++i) 37 ptr[i]=vi.ptr[i]; 38 } 39 vectorInt::~vectorInt() 40 { 41 delete [] ptr; 42 } 43 int vectorInt::size() const 44 { 45 return n; 46 } 47 const int &vectorInt::at(int index) const 48 { 49 if(index<0||index>=n) 50 { 51 std::cerr<<"IndexError:index out of range\n"; 52 std::exit(1); 53 } 54 return ptr[index]; 55 } 56 int& vectorInt::at(int index) 57 { 58 if(index<0||index>=n) 59 { 60 std::cerr<<"IndexError:index out of range\n"; 61 std::exit(1); 62 } 63 return ptr[index]; 64 } 65 vectorInt& vectorInt::assign(const vectorInt &vi) 66 { 67 if(this==&vi) 68 return *this; 69 int *ptr_tmp; 70 ptr_tmp=new int[vi.n]; 71 for(int i=0;i<vi.n;++i) 72 ptr_tmp[i]=vi.ptr[i]; 73 delete [] ptr; 74 n=vi.n; 75 ptr=ptr_tmp; 76 return *this; 77 } 78 int *vectorInt::begin() 79 { 80 return ptr; 81 } 82 int *vectorInt::end() 83 { 84 return ptr+n; 85 } 86 const int* vectorInt::begin() const 87 { 88 return ptr; 89 } 90 const int* vectorInt::end() const 91 { 92 return ptr+n; 93 }
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 { 9 std::cout << "测试1: \n"; 10 test1(); 11 std::cout << "\n测试2: \n"; 12 test2(); 13 } 14 void test1() 15 { 16 int n; 17 std::cout << "Enter n: "; 18 std::cin >> n; 19 vectorInt x1(n); 20 for(auto i=0;i<n;++i) 21 x1.at(i)=(i+1)*10; 22 std::cout<<"x1: "; 23 output1(x1); 24 vectorInt x2(n,42); 25 vectorInt x3(x2); 26 x2.at(0)=-1; 27 std::cout<<"x2: "; 28 output1(x2); 29 std::cout<<"x3: "; 30 output1(x3); 31 } 32 void test2() 33 { 34 const vectorInt x(5,42); 35 vectorInt y; 36 y.assign(x); 37 std::cout<<"x: "; 38 output2(x); 39 std::cout<<"y: "; 40 output2(y); 41 } 42 void output1(const vectorInt &vi) 43 { 44 if(vi.size()==0) 45 { 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 void output2(const vectorInt &vi) 55 { 56 if(vi.size()==0) 57 { 58 std::cout<<'\n'; 59 return; 60 } 61 auto it=vi.begin(); 62 std::cout<<*it; 63 for(it=vi.begin()+1;it!=vi.end();++it) 64 std::cout<<", "<<*it; 65 std::cout<<'\n'; 66 }
运行结果截图

问题1:当前验证性代码中, vectorInt 接口 assign 实现是安全版本。如果把 assign 实现改成版本2, 逐条指出版本 2存在的安全隐患和缺陷。(提示:对比两个版本,找出差异化代码,加以分析)
答:版本二先执行delete[] ptr;,释放ptr 。随后,当访问 vi.ptr[i]时,会产生未定义错误。
版本二 先delete后再调用了new,如果重新分配失败,会泄露数据
版本二在最开始delete[] ptr;后面不再处理ptr
问题2:当前验证性代码中,重载接口at内部代码完全相同。若把非 const 版本改成如下实现,可消除重复并遵循“最小化接口”原则(未来如需更新接口,只更新const接口,另一个会同步)。
查阅资料,回答:
(1)static_cast<const vectorInt*>(this) 的作用是什么?转换前后this 的类型分别是什么?转换目的?
答:转换前this 的类型是 vectorInt*,转换后:const vectorInt*,目的:通过将 this 转换为指向const类型的指针,可调用const函数
(2)const_cast<int&> 的作用是什么?转换前后的返回类型分别是什么?转换目的?
答:转换前返回的是 const int&。转换后是 int&,目的:因为原对象是非const的,要返回非const的引用,所以通过const_cast将const引用转换为非const引用
问题3:vectorInt类封装了begin()和end()的const/非const接口。
(1)以下代码片段,分析编译器如何选择重载版本,并总结这两种重载分别适配什么使用场景。
答:由于v1非const对象,选择非const的begin(),由于v2是const对象选择const版本的begin()。
非const用于需要修内容的场景,const版本:用于读取但不修改的场景。
问题4:以下两个构造函数及assign接口实现,都包含内存块的赋值/复制操作。使用算法库<algorithm>改写是否可以?回答这3行更新代码的功能
答:可以,std::fill_n(ptr, n, value)是将ptr中 n 个元素都置为 value
std::copy_n(vi.ptr, vi.n, ptr)将vi.ptr中 n 个元素复制到ptr中
std::copy_n(vi.ptr, vi.n, ptr_tmp)将vi.ptr中 n 个元素复制到临时创建的ptr_tmp中
任务四:
matrix.hpp
1 #pragma once 2 class Matrix 3 { 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 void set(const double *pvalue,int size); 10 void clear(); 11 const double& at(int i,int j) const; 12 double& at(int i,int j); 13 int rows() const; 14 int cols() const; 15 void print() const; 16 private: 17 int n_rows; 18 int n_cols; 19 double *ptr; 20 };
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[rows_*cols_]} 5 { 6 for(auto i=0;i<n_rows*n_cols;++i) 7 ptr[i]=value; 8 } 9 Matrix::Matrix(int rows_, double value ):n_rows{rows_},n_cols{rows_},ptr{new double[n_rows*n_cols]} 10 { 11 for(auto i=0;i<n_rows*n_rows;++i) 12 ptr[i]=value; 13 } 14 Matrix::Matrix(const Matrix &x):n_rows{x.n_rows},n_cols{x.n_cols},ptr{new double[x.n_cols*x.n_rows]} 15 { 16 for(auto i=0;i<n_rows*n_cols;i++) 17 18 ptr[i]=x.ptr[i]; 19 } 20 Matrix::~Matrix() 21 { 22 delete [] ptr; 23 } 24 void Matrix::set(const double *pvalue, int size) 25 { 26 if(size!=rows()*cols()) 27 exit(1); 28 for(auto i=0;i<size;++i) 29 ptr[i]=pvalue[i]; 30 } 31 void Matrix::clear() 32 { 33 for(int i = 0; i < rows() * cols(); ++i) 34 ptr[i] = 0.0; 35 } 36 const double& Matrix::at(int i, int j) const 37 { 38 if(i<0||i>=rows()||j<0||j>=cols()) 39 exit(1); 40 else 41 return ptr[i*cols()+j]; 42 43 } 44 45 double& Matrix::at(int i, int j) 46 { 47 if(i<0||i>=rows()||j<0||j>=cols()) 48 exit(1); 49 else 50 return ptr[i*cols()+j]; 51 } 52 53 int Matrix::rows() const 54 { 55 return n_rows; 56 } 57 int Matrix::cols() const 58 { 59 return n_cols; 60 } 61 void Matrix::print() const 62 { 63 int i,j; 64 for( i=0;i<rows();i++) 65 { 66 for( j=0;j<cols();j++) 67 { 68 std::cout<<ptr[i*cols()+j]; 69 if(j < cols() - 1) 70 std::cout << ", "; 71 } 72 73 std::cout<<std::endl; 74 }}
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 { 16 double x[1000] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 17 int n, m; 18 std::cout << "Enter n and m: "; 19 std::cin >> n >> m; 20 Matrix m1(n,m); 21 m1.set(x,n*m); 22 Matrix m2(m,n); 23 m2.set(x,m*n); 24 Matrix m3(n); 25 m3.set(x,n*n); 26 std::cout << "矩阵对象m1: \n"; 27 m1.print(); 28 std::cout << "矩阵对象m2: \n"; 29 m2.print(); 30 std::cout << "矩阵对象m3: \n"; 31 m3.print(); 32 } 33 void test2() 34 { 35 Matrix m1(2,3,-1); 36 const Matrix m2(m1); 37 std::cout << "矩阵对象m1: \n"; 38 m1.print(); 39 std::cout << "矩阵对象m2: \n"; 40 m2.print(); 41 m1.clear(); 42 m1.at(0,0)=1; 43 std::cout<<"m1更新后: \n"; 44 std::cout<<"矩阵对象m1第0行 "; 45 output(m1, 0); 46 std::cout << "矩阵对象m2第0行: "; 47 output(m2, 0); 48 } 49 void output(const Matrix &m,int row_index) 50 { 51 if(row_index<0||row_index>m.rows()) 52 { 53 std::cerr<<"IndexError: row index out of range\n"; 54 std::exit(1); 55 } 56 std::cout<<m.at(row_index,0); 57 for(int j=1;j<m.cols();++j) 58 std::cout<<", "<<m.at(row_index,j); 59 std::cout<<'\n'; 60 }
运行结果截图

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


浙公网安备 33010602011771号