实验三

任务一:

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  }

运行结果截图

屏幕截图 2025-11-25 214943

 

posted @ 2025-11-25 22:32  零和星际  阅读(6)  评论(1)    收藏  举报