实验3
任务一

问题1:这个范例中,Window和Button是组合关系吗?
答::::是的。Window 类包含了一个 std::vector<Button> 成员变量 buttons。这意味着一个 Window 对象拥有(owns)多个 Button 对象,并且这些 Button 对象的生命周期由 Window 对象管理。当 Window 对象被销毁时,其内部的所有 Button 对象也会随之被销毁。
问题2: bool has_button(const std::string &label) const; 被设计为私有。 思考并回答:
答:::: (1)若将其改为公有接口,有何优点或风险?
优点:外部用户可主动查询某个按钮是否存在,增强类的灵活性
风险:暴露了内部实现细节
(2)设计类时,如何判断一个成员函数应为 public 还是 private?(可从“用户是否需要”、“是否仅为内部实现 细节”、“是否易破坏对象状态”等角度分析。)
若该函数是面向用户的必要操作(如 add_button, click_button),应为 public。若仅为内部辅助逻辑(如检查重复、排序等),且用户无需也不应直接调用,则应为 private。若暴露后易破坏对象状态一致性(如允许外部直接修改内部容器),则必须隐藏。
问题3: Button 的接口 两种接口设计在性能和安全性方面的差异并精炼陈述。 接口1: const std::string& get_label() const;返回 const std::string& get_label() const; 接口2: const std::string get_label() const;
答::::接口1(const std::string&):性能:避免拷贝,直接返回引用,高效。安全性:返回 const 引用,禁止外部修改原始数据,安全
接口2(const std::string):性能:每次调用都构造并返回一个新 string 对象,有拷贝开销。安全性:同样不可修改原始数据,但效率低。
问题4:把代码中所有 const std::string& 。对比以下 xx.push_back(Button(xxx)) 改成 xx.emplace_back(xxx) ,观察程序是否正常运 行;查阅资料,回答两种写法的差别
答:::可行,程序正常运行。差别:push_back(Button(xxx)):先构造临时 Button 对象,再将其拷贝或移动进容器。emplace_back(xxx):直接在容器内存中原位构造 Button 对象,避免临时对象和拷贝/移动开销。优势:emplace_back 更高效,尤其对复杂对象。
任务二

问题1:test1() 中两行代码完成什么构造?v1、v2 各有多少个 42?
std::vector<int> v1(5, 42);:调用 填充构造函数,创建含 5 个 42 的 v1。const std::vector<int> v2(v1);:调用 拷贝构造函数,v2 也含 5 个 42。
问题2:test2() 中执行后,v1.size()、v2.size()、v1[0].size() 分别是多少
v1.size() = 2(外层 vector 有 2 行)v2.size() = 2(拷贝后相同)v1[0].size() = 3(第一行 {1,2,3})
问题3:v1.at(0) = -1; 与 v1[0] = -1; 效果是否相同?区别?
效果相同(都能赋值)。
区别:at() 带边界检查,越界抛出 std::out_of_range 异常。operator[] 无检查,越界行为未定义(可能崩溃或数据损坏)。 调试/安全场景优先用 at()。
问题4:执行 v1.at(0).push_back(-1); 后:
(1)能否用以下代码输出 -1?为什么?
r 是 vector<int> 的拷贝(值语义),r.back() 输出的是原 v1[0] 修改前的最后一个元素(7),而非 -1。正确做法:用 const auto& r = v1.at(0); 获取引用。
(2)用 const& 接收返回值的优势与限制?
优势:避免拷贝,节省内存和时间。
限制:不能通过该引用修改对象(因是 const)。
问题5:推断 vector 的复制行为及 at() 重载必要性
(1)vector 复制是深复制:修改 v1 不影响 v2,说明内部数据独立。
(2)at() 必须提供 const 重载:
非 const 对象调用 at() 返回 T&(可修改)。const 对象调用 at() 返回 const T&(只读)。若无 const 重载,const 对象无法调用 at(),违反 const 正确性。
任务三
问题1:assign 版本2的安全隐患?
缺陷:
- 未处理自赋值(
this == &vi):若vi就是当前对象,delete[] ptr后vi.ptr已失效,后续读取vi.ptr[i]是未定义行为。 - 异常不安全:若
new int[n]抛异常(如内存不足),ptr已被删除,对象处于无效状态(资源泄漏 + 悬空指针)。
问题2:关于 static_cast 和 const_cast 的作用
(1)static_cast<const vectorInt*>(this):
作用:将 this(类型 vectorInt*)转为 const vectorInt*。
目的:在非 const 成员函数中,复用 const 版本的 at() 实现,避免代码重复。
转换后类型:const vectorInt*。
(2)const_cast<int&>(...):
作用:去除 const 限定,将 const int& 转为 int&。
目的:使非 const at() 能返回可修改的引用。
转换前类型:const int&;后:int&。
问题3:begin() 重载选择与迭代器理解
(1)重载选择:vectorInt v1(5); → v1.begin() 调用 非 const 版本(返回 int*)。const vectorInt v2(5); → v2.begin() 调用 const 版本(返回 const int*)。
原则:根据对象是否 const 决定调用哪个重载。
(2)拓展思考:
迭代器本质是泛化的指针。vectorInt 直接返回原始指针,说明简单容器可用指针作为迭代器,体现“零成本抽象”——高性能且接口统一。
问题4:使用 <algorithm> 改写是否可行?功能解释
可行。三行更新代码功能:std::fill_n(ptr, n, value);:将 ptr 开始的 n 个元素赋值为 value。std::copy_n(vi.ptr, vi.n, ptr);:从 vi.ptr 复制 vi.n 个元素到 ptr。
同上,用于 assign 中的临时缓冲区填充。
使用标准算法更简洁、安全、高效。
任务四
matrix.cpp
1 #include "matrix.hpp" 2 #include <iostream> 3 #include <cstdlib> // for std::exit 4 #include <algorithm> // for std::fill_n 5 6 // 辅助函数:检查索引是否越界 7 void check_bounds(int i, int j, int rows, int cols) { 8 if (i < 0 || i >= rows || j < 0 || j >= cols) { 9 std::cerr << "IndexError: index out of range\n"; 10 std::exit(1); 11 } 12 } 13 14 // 构造 rows × cols 矩阵 15 Matrix::Matrix(int rows_, int cols_, double value) 16 : n_rows(rows_), n_cols(cols_), ptr(new double[rows_ * cols_]) { 17 std::fill_n(ptr, rows_ * cols_, value); 18 } 19 20 // 构造方阵 rows × rows 21 Matrix::Matrix(int rows_, double value) 22 : Matrix(rows_, rows_, value) {} // 委托构造 23 24 // 深拷贝构造函数 25 Matrix::Matrix(const Matrix& x) 26 : n_rows(x.n_rows), n_cols(x.n_cols), ptr(new double[n_rows * n_cols]) { 27 std::copy_n(x.ptr, n_rows * n_cols, ptr); 28 } 29 30 // 析构函数 31 Matrix::~Matrix() { 32 delete[] ptr; 33 } 34 35 // 按行设置数据 36 void Matrix::set(const double* pvalue, int size) { 37 if (size != n_rows * n_cols) { 38 std::cerr << "SizeError: size must be rows * cols\n"; 39 std::exit(1); 40 } 41 std::copy_n(pvalue, size, ptr); 42 } 43 44 // 清零 45 void Matrix::clear() { 46 std::fill_n(ptr, n_rows * n_cols, 0.0); 47 } 48 49 // const 版 at 50 const double& Matrix::at(int i, int j) const { 51 check_bounds(i, j, n_rows, n_cols); 52 return ptr[i * n_cols + j]; 53 } 54 55 // 非 const 版 at 56 double& Matrix::at(int i, int j) { 57 check_bounds(i, j, n_rows, n_cols); 58 return ptr[i * n_cols + j]; 59 } 60 61 // 获取行数 62 int Matrix::rows() const { 63 return n_rows; 64 } 65 66 // 获取列数 67 int Matrix::cols() const { 68 return n_cols; 69 } 70 71 // 按行打印矩阵 72 void Matrix::print() const { 73 for (int i = 0; i < n_rows; ++i) { 74 for (int j = 0; j < n_cols; ++j) { 75 std::cout << at(i, j); 76 if (j < n_cols - 1) std::cout << " "; 77 } 78 std::cout << '\n'; 79 } 80 }
结果

任务五
#pragma once #include "contact.hpp" #include <vector> #include <string> #include <algorithm> #include <iostream> class ContactBook { public: void add(const std::string& name, const std::string& phone) { if (index(name) != -1) { std::cout << name << " already exists. fail to add!\n"; return; } contacts.emplace_back(name, phone); sort(); std::cout << name << " add successfully.\n"; } void display() const { std::cout << "There are " << contacts.size() << " contacts.\n"; for (const auto& c : contacts) { std::cout << c.get_name() << "," << c.get_phone() << "\n"; } } void find(const std::string& name) const { int idx = index(name); if (idx == -1) { std::cout << name << " not found!\n"; } else { std::cout << contacts[idx].get_name() << "," << contacts[idx].get_phone() << "\n"; } } void remove(const std::string& name) { int idx = index(name); if (idx == -1) { std::cout << name << " not found, fail to remove!\n"; } else { contacts.erase(contacts.begin() + idx); std::cout << name << " remove successfully.\n"; } } private: std::vector<Contact> contacts; // 待补足1:返回联系人索引,不存在返回-1 int index(const std::string& name) const { for (size_t i = 0; i < contacts.size(); ++i) { if (contacts[i].get_name() == name) { return static_cast<int>(i); } } return -1; } // 待补足2:按姓名字典序升序排序 void sort() { std::sort(contacts.begin(), contacts.end(), [](const Contact& a, const Contact& b) { return a.get_name() < b.get_name(); }); } };
#include "matrix.hpp"#include <iostream>#include <cstdlib> // for std::exit#include <algorithm> // for std::fill_n
// 辅助函数:检查索引是否越界void check_bounds(int i, int j, int rows, int cols) { if (i < 0 || i >= rows || j < 0 || j >= cols) { std::cerr << "IndexError: index out of range\n"; std::exit(1); }}
// 构造 rows × cols 矩阵Matrix::Matrix(int rows_, int cols_, double value) : n_rows(rows_), n_cols(cols_), ptr(new double[rows_ * cols_]) { std::fill_n(ptr, rows_ * cols_, value);}
// 构造方阵 rows × rowsMatrix::Matrix(int rows_, double value) : Matrix(rows_, rows_, value) {} // 委托构造
// 深拷贝构造函数Matrix::Matrix(const Matrix& x) : n_rows(x.n_rows), n_cols(x.n_cols), ptr(new double[n_rows * n_cols]) { std::copy_n(x.ptr, n_rows * n_cols, ptr);}
// 析构函数Matrix::~Matrix() { delete[] ptr;}
// 按行设置数据void Matrix::set(const double* pvalue, int size) { if (size != n_rows * n_cols) { std::cerr << "SizeError: size must be rows * cols\n"; std::exit(1); } std::copy_n(pvalue, size, ptr);}
// 清零void Matrix::clear() { std::fill_n(ptr, n_rows * n_cols, 0.0);}
// const 版 atconst double& Matrix::at(int i, int j) const { check_bounds(i, j, n_rows, n_cols); return ptr[i * n_cols + j];}
// 非 const 版 atdouble& Matrix::at(int i, int j) { check_bounds(i, j, n_rows, n_cols); return ptr[i * n_cols + j];}
// 获取行数int Matrix::rows() const { return n_rows;}
// 获取列数int Matrix::cols() const { return n_cols;}
// 按行打印矩阵void Matrix::print() const { for (int i = 0; i < n_rows; ++i) { for (int j = 0; j < n_cols; ++j) { std::cout << at(i, j); if (j < n_cols - 1) std::cout << " "; } std::cout << '\n'; }}
浙公网安备 33010602011771号