实验3

实验任务1

window.hpp

#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include "button.hpp"
// 窗口类
class Window{
public:
Window(const std::string &title_);
void display() const;
void close();
void add_button(const std::string &label);
void click_button(const std::string &label);
private:
bool has_button(const std::string &label) const;
private:
std::string title;
inline bool Window::has_button(const std::string &label) const {
for(const auto &button: buttons)
if(button.get_label() == label)
return true;
return false;
}
inline void Window::add_button(const std::string &label) {
if(has_button(label))
std::cout << "button " << label << " already exists!\n";
else
buttons.push_back(Button(label));
}
inline void Window::click_button(const std::string &label) {
for(auto &button:buttons)
if(button.get_label() == label) {
button.click();
return;
}
std::cout << "no button: " << label << std::endl;
}
std::vector<Button> buttons;
};
Window::Window(const std::string &title_): title{title_} {
buttons.push_back(Button("close"));
}
inline void Window::display() const {
std::string s(40, '*');
std::cout << s << std::endl;
std::cout << "window : " << title << std::endl;
int cnt = 0;
for(const auto &button: buttons)
std::cout << ++cnt << ". " << button.get_label() << std::endl;
std::cout << s << std::endl;
}
inline void Window::close() {
std::cout << "close window '" << title << "'" << std::endl;
click_button("close");
}
 
button.hpp
#pragma once
#include <iostream>
#include <string>
class Button {
public:
    Button(const std::string& label_);
    const std::string& get_label() const;
    void click();
private:
    std::string label;
};
Button::Button(const std::string& label_) : label{ label_ } {
}
inline const std::string& Button::get_label() const {
    return label;
}
inline void Button::click() {
    std::cout << "Button '" << label << "' clicked\n";
}
task1.cpp
#include "window.hpp"
#include <iostream>
void test() {
    Window w("Demo");
    w.add_button("add");
    w.add_button("remove");
    w.add_button("modify");
    w.add_button("add");
    w.display();
    w.close();
}
int main() {
    std::cout << "用组合类模拟简单GUI:\n";
    test();
}

image

 问题

1: 是组合关系

2:隐藏了实现细节,避免外部直接检查,保证内部约束

3:前者避免了拷贝,高效;后者生成临时对象,易引发悬垂引用,性能差。

4:均可正常运行,emplace_back更高效

 

实验任务2

task2:

#include <iostream>
#include <vector>
void test1();
void test2();
void output1(const std::vector<int>& v);
void output2(const std::vector<int>& v);
void output3(const std::vector<std::vector<int>>& v);
int main() {
    std::cout << "深复制验证1: 标准库vector<int>\n";
    test1();
    std::cout << "\n深复制验证2: 标准库vector<int>嵌套使用\n";
    test2();
}
void test1() {
    std::vector<int> v1(5, 42);
    const std::vector<int> v2(v1);
    std::cout << "**********拷贝构造后**********\n";
    std::cout << "v1: "; output1(v1);
    std::cout << "v2: "; output1(v2);
    v1.at(0) = -1;
    std::cout << "**********修改v1[0]后**********\n";
    std::cout << "v1: "; output1(v1);
    std::cout << "v2: "; output1(v2);
}
void test2() {
    std::vector<std::vector<int>> v1{ {1, 2, 3}, {4, 5, 6, 7} };
    const std::vector<std::vector<int>> v2(v1);
    std::cout << "**********拷贝构造后**********\n";
    std::cout << "v1: "; output3(v1);
    std::cout << "v2: "; output3(v2);
    v1.at(0).push_back(-1);
    std::cout << "**********修改v1[0]后**********\n";
    std::cout << "v1: \n"; output3(v1);
    std::cout << "v2: \n"; output3(v2);
}
// 使用xx.at()+循环输出vector<int>数据项
void output1(const std::vector<int>& v) {
    if (v.size() == 0) {
        std::cout << '\n';
        return;
    }
    std::cout << v.at(0);
    for (auto i = 1; i < v.size(); ++i)
        std::cout << ", " << v.at(i);
    std::cout << '\n';
}
// 使用迭代器+循环输出vector<int>数据项
void output2(const std::vector<int>& v) {
    if (v.size() == 0) {
        std::cout << '\n';
        return;
    }
    auto it = v.begin();
    std::cout << *it;
    for (it = v.begin() + 1; it != v.end(); ++it)
        std::cout << ", " << *it;
    std::cout << '\n';
}
// 使用auto for分行输出vector<vector<int>>数据项
void output3(const std::vector<std::vector<int>>& v) {
    if (v.size() == 0) {
        std::cout << '\n';
        return;
    }
    for (auto& i : v)
        output2(i);
}

image

 问题

1:V1和V2均含5个42

2:v1.size()==2,v2.size()==2,v1[0].size++3

3:at(0)与【0】效果相同,at带有边界检查

4:(1)

可以。
v1.at(0).push_back(-1);  已把  -1  放进  v1[0]  的末尾, r  是该 vector 的别名,因此  r.size()-1  对应下标即为  -1 ,能够正确输出。

(2)

优势:绑定到  at()  返回的  const vector<int>& ,避免拷贝,节省内存;同时禁止通过  r  修改容器,提高安全性。

限制:不能再通过  r  调用非  const  接口(如  push_back 、 clear  等),只能进行只读访问。

5:(1)

是深复制

(2)

是int&;是const int&。

必须提供

 

实验任务3

vectorInt.hpp
#pragma once
#include <iostream>
// 动态int数组对象类
class vectorInt {
public:
    vectorInt();
    vectorInt(int n_);
    vectorInt(int n_, int value);
    vectorInt(const vectorInt& vi);
    ~vectorInt();
    int size() const;
    int& at(int index);
    const int& at(int index) const;
    vectorInt& assign(const vectorInt& vi);
    int* begin();
    int* end();
    const int* begin() const;
    const int* end() const;
private:
    int n; // 当前数据项个数
    int* ptr; // 数据区
};
vectorInt::vectorInt() :n{ 0 }, ptr{ nullptr } {
}
vectorInt::vectorInt(int n_) : n{ n_ }, ptr{ new int[n] } {
}
vectorInt::vectorInt(int n_, int value) : n{ n_ }, ptr{ new int[n_] } {
    for (auto i = 0; i < n; ++i)
        ptr[i] = value;
}
vectorInt::vectorInt(const vectorInt& vi) : n{ vi.n }, ptr{ new int[n] } {
    for (auto i = 0; i < n; ++i)
        ptr[i] = vi.ptr[i];
}
vectorInt::~vectorInt() {
    delete[] ptr;
}
int vectorInt::size() const {
    return n;
}
const int& vectorInt::at(int index) const {
    if (index < 0 || index >= n) {
        std::cerr << "IndexError: index out of range\n";
        std::exit(1);
    }
    return ptr[index];
}
int& vectorInt::at(int index) {
    if (index < 0 || index >= n) {
        std::cerr << "IndexError: index out of range\n";
        std::exit(1);
    }
    return ptr[index];
}
vectorInt& vectorInt::assign(const vectorInt& vi) {
    if (this == &vi)
        return *this;
    int* ptr_tmp;
    ptr_tmp = new int[vi.n];
    for (int i = 0; i < vi.n; ++i)
        ptr_tmp[i] = vi.ptr[i];
    delete[] ptr;
    n = vi.n;
    ptr = ptr_tmp;
    return *this;
}
int* vectorInt::begin() {
    return ptr;
}
int* vectorInt::end() {
    return ptr + n;
}
const int* vectorInt::begin() const {
    return ptr;
}
const int* vectorInt::end() const {
    return ptr + n;
}

task3.cpp

#include "vectorInt.hpp"
#include <iostream>
void test1();
void test2();
void output1(const vectorInt& vi);
void output2(const vectorInt& vi);
int main() {
    std::cout << "测试1: \n";
    test1();
    std::cout << "\n测试2: \n";
    test2();
}
void test1() {
    int n;
    std::cout << "Enter n: ";
    std::cin >> n;
    vectorInt x1(n);
    for (auto i = 0; i < n; ++i)
        x1.at(i) = (i + 1) * 10;
    std::cout << "x1: "; output1(x1);
    vectorInt x2(n, 42);
    vectorInt x3(x2);
    x2.at(0) = -1;
    std::cout << "x2: "; output1(x2);
    std::cout << "x3: "; output1(x3);
}
void test2() {
    const vectorInt x(5, 42);
    vectorInt y;
    y.assign(x);
    std::cout << "x: "; output2(x);
    std::cout << "y: "; output2(y);
}
// 使用xx.at()+循环输出vectorInt对象数据项
void output1(const vectorInt& vi) {
    if (vi.size() == 0) {
        std::cout << '\n';
        return;
    }
    std::cout << vi.at(0);
    for (auto i = 1; i < vi.size(); ++i)
        std::cout << ", " << vi.at(i);
    std::cout << '\n';
}
// 使用迭代器+循环输出vectorInt对象数据项
void output2(const vectorInt& vi) {
    if (vi.size() == 0) {
        std::cout << '\n';
        return;
    }
    auto it = vi.begin();
    std::cout << *it;
    for (it = vi.begin() + 1; it != vi.end(); ++it)
        std::cout << ", " << *it;
    std::cout << '\n';
}

image

 问题

1:

自赋值未判断:若  &vi == this ,① 会把自身内存先释放,后续  vi.ptr[i]  成悬空访问。

异常不安全:若  new int[n]  抛  std::bad_alloc ,则  ptr  已指向被释放的内存,对象进入不可恢复状态。

内存泄漏:当  vi.n > 原 n  且  new  失败时,原内存已被  delete[] ,却未回滚,导致后续析构时二次  delete[] 。

2:(1)

调用已存在的const重载版本,实现代码复用

(2)

转换前类型: const int& (const 版 at 的返回类型)

转换后类型: int& 

目的:移除返回值的 const 限定,使非 const 对象也能获得可写引用,符合接口语义。

3:(1)

非const用于读写,const用于只读

(2)

vectorInt 直接返回原始指针作为迭代器,说明迭代器本质上是指针的薄封装。只要内存连续,原始指针即可满足迭代器概念,体现“零成本抽象”。

4:

可以改写

第一行把[ptr,ptr+n)全部置为value

第二行把vi的全部元素深复制到当前对象

第三行先复制到临时缓冲区,保障异常安全

 

实验任务4

matrix.hpp
#pragma once
// 类Matrix声明
class Matrix {
public:
    Matrix(int rows_, int cols_, double value = 0); // 构造rows_*cols_矩阵对象, 初值value
    Matrix(int rows_, double value = 0); // 构造rows_*rows_方阵对象, 初值value
    Matrix(const Matrix& x); // 深复制
    ~Matrix();
    void set(const double* pvalue, int size); // 按行复制pvalue指向的数据,要求size = rows * cols, 否则报错退出
        void clear(); // 矩阵对象数据项置0
    const double& at(int i, int j) const; // 返回矩阵对象索引(i,j)对应的数据项const引用(越界则报错后退出)
        double& at(int i, int j); // 返回矩阵对象索引(i,j)对应的数据项引用(越界则报错后退出)
    int rows() const; // 返回矩阵对象行数
    int cols() const; // 返回矩阵对象列数
    void print() const; // 按行打印数据
private:
    int n_rows; // 矩阵对象内元素行数
    int n_cols; // 矩阵对象内元素列数
    double* ptr; // 数据区
};

task4

#include <iostream>
#include <cstdlib>
#include "matrix.hpp"
void test1();
void test2();
void output(const Matrix& m, int row_index);
int main() {
    std::cout << "测试1: \n";
    test1();
    std::cout << "\n测试2: \n";
    test2();
}
void test1() {
    double x[1000] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int n, m;
    std::cout << "Enter n and m: ";
    std::cin >> n >> m;
    Matrix m1(n, m); // 创建矩阵对象m1, 大小n×m
    m1.set(x, n * m); // 用一维数组x的值按行为矩阵m1赋值
    Matrix m2(m, n); // 创建矩阵对象m2, 大小m×n
    m2.set(x, m * n); // 用一维数组x的值按行为矩阵m1赋值
    Matrix m3(n); // 创建一个n×n方阵对象
    m3.set(x, n * n); // 用一维数组x的值按行为矩阵m3赋值
    std::cout << "矩阵对象m1: \n"; m1.print();
    std::cout << "矩阵对象m2: \n"; m2.print();
    std::cout << "矩阵对象m3: \n"; m3.print();
}
void test2() {
    Matrix m1(2, 3, -1);
    const Matrix m2(m1);
    std::cout << "矩阵对象m1: \n"; m1.print();
    std::cout << "矩阵对象m2: \n"; m2.print();
    m1.clear();
    m1.at(0, 0) = 1;
    std::cout << "m1更新后: \n";
    std::cout << "矩阵对象m1第0行 "; output(m1, 0);
    std::cout << "矩阵对象m2第0行: "; output(m2, 0);
}
// 输出矩阵对象row_index行所有元素
void output(const Matrix& m, int row_index) {
    if (row_index < 0 || row_index > m.rows()) {
        std::cerr << "IndexError: row index out of range\n";
        std::exit(1);
    }
    std::cout << m.at(row_index, 0);
    for (int j = 1; j < m.cols(); ++j)
        std::cout << ", " << m.at(row_index, j);
    std::cout << '\n';
}
matrix.cpp
#include "matrix.hpp"
#include <iostream>
#include <cstdlib>
#include <algorithm>

Matrix::Matrix(int rows_, int cols_, double value)
    : n_rows(rows_), n_cols(cols_), ptr(new double[rows_ * cols_]) {
    std::fill_n(ptr, n_rows * n_cols, value);
}

Matrix::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 << "Size mismatch in set()\n";
        std::exit(1);
    }
    std::copy_n(pvalue, size, ptr);
}

void Matrix::clear() {
    std::fill_n(ptr, n_rows * n_cols, 0.0);
}

const double& Matrix::at(int i, int j) const {
    if (i < 0 || i >= n_rows || j < 0 || j >= n_cols) {
        std::cerr << "IndexError: matrix subscript out of bounds\n";
        std::exit(1);
    }
    return ptr[i * n_cols + j];
}

double& Matrix::at(int i, int j) {
    if (i < 0 || i >= n_rows || j < 0 || j >= n_cols) {
        std::cerr << "IndexError: matrix subscript out of bounds\n";
        std::exit(1);
    }
    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 << ptr[i * n_cols + j];
            if (j + 1 < n_cols) std::cout << ", ";
        }
        std::cout << "\n";
    }
}

image

 

实验任务5

contact.hpp
#pragma once
#include <iostream>
#include <string>
// 联系人类
class Contact {
public:
    Contact(const std::string& name_, const std::string& phone_);
    const std::string& get_name() const;
    const std::string& get_phone() const;
    void display() const;
private:
    std::string name; // 必填项
    std::string phone; // 必填项
};
Contact::Contact(const std::string& name_, const std::string& phone_) :name{ name_ },
phone{ phone_ } {
}
const std::string& Contact::get_name() const {
    return name;
}
const std::string& Contact::get_phone() const {
    return phone;
}
void Contact::display() const {
    std::cout << name << ", " << phone;
}
contactBook.hpp
# pragma once
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "contact.hpp"
// 通讯录类
class ContactBook {
public:
    void add(const std::string& name, const std::string& phone); // 添加联系人
    void remove(const std::string& name); // 移除联系人
    void find(const std::string& name) const; // 查找联系人
    void display() const; // 显示所有联系人
    size_t size() const;
private:
    
    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;
    }
    void sort() {
        std::sort(contacts.begin(), contacts.end(),
            [](const Contact& a, const Contact& b) {
                return a.get_name() < b.get_name();
            });
    }
private:
    std::vector<Contact> contacts;
};
void ContactBook::add(const std::string& name, const std::string& phone) {
    if (index(name) == -1) {
        contacts.push_back(Contact(name, phone));
        std::cout << name << " add successfully.\n";
        sort();
        return;
    }
    std::cout << name << " already exists. fail to add!\n";
}
void ContactBook::remove(const std::string& name) {
    int i = index(name);
    if (i == -1) {
        std::cout << name << " not found, fail to remove!\n";
        return;
    }
    contacts.erase(contacts.begin() + i);
    std::cout << name << " remove successfully.\n";
}
void ContactBook::find(const std::string& name) const {
    int i = index(name);
    if (i == -1) {
        std::cout << name << " not found!\n";
        return;
    }
    contacts[i].display();
    std::cout << '\n';
}
void ContactBook::display() const {
    for (auto& c : contacts) {
        c.display();
        std::cout << '\n';
    }
}
size_t ContactBook::size() const {
    return contacts.size();
}

task5

#include "contactBook.hpp"
void test() {
    ContactBook contactbook;
    std::cout << "1. add contacts\n";
    contactbook.add("Bob", "18199357253");
    contactbook.add("Alice", "17300886371");
    contactbook.add("Linda", "18184538072");
    contactbook.add("Alice", "17300886371");
    std::cout << "\n2. display contacts\n";
    std::cout << "There are " << contactbook.size() << " contacts.\n";
    contactbook.display();
    std::cout << "\n3. find contacts\n";
    contactbook.find("Bob");
    contactbook.find("David");
    std::cout << "\n4. remove contact\n";
    contactbook.remove("Bob");
    contactbook.remove("David");
}
int main() {
    test();
}

image

 

posted @ 2025-11-26 00:56  15468483  阅读(2)  评论(1)    收藏  举报