实验三

实验任务一:

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

运行结果:

image

问题一:是组合关系。

问题二:

   (1):优点:外部可直接查询按钮是否存在;更方便,更便捷;

            风险:暴露内部实现细节;违背 “封装原则”:类的设计应隐藏内部状态和辅助逻辑,仅暴露必要的功能接口。 

   (2):1.是否为外部必需的功能:若函数是类对外提供的核心服务,需设为 public;若仅为类内部其他成员函数提供辅助,则设为 private;

    2.是否涉及内部实现细节:若函数依赖类的内部存储结构、逻辑,设为 private,避免外部依赖导致内部实现无法灵活修改;

问题三:

接口 1:无拷贝,性能高;const限制不可改,安全性高。

接口 2:值返回需拷贝,性能低;仅不影响原对象,安全性一般。

问题四:能正常运行,差别:push_back先创临时对象再拷贝,有额外步骤和空间;emplace_back直接在容器内构造,无临时对象,性能更优。

实验任务二:

 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 
 9 int main() {
10     std::cout << "深复制验证1: 标准库vector<int>\n";
11     test1();
12     std::cout << "\n深复制验证2: 标准库vector<int>嵌套使用\n";
13     test2();
14 }
15 
16 void test1() {
17     std::vector<int> v1(5, 42);
18     const std::vector<int> v2(v1);
19     std::cout << "**********拷贝构造后**********\n";
20     std::cout << "v1: "; output1(v1);
21     std::cout << "v2: "; output1(v2);
22     v1.at(0) = -1;
23     std::cout << "**********修改v1[0]后**********\n";
24     std::cout << "v1: "; output1(v1);
25     std::cout << "v2: "; output1(v2);
26 }
27 
28 void test2() {
29     std::vector<std::vector<int>> v1{{1, 2, 3}, {4, 5, 6, 7}};
30     const std::vector<std::vector<int>> v2(v1);
31     std::cout << "**********拷贝构造后**********\n";
32     std::cout << "v1: "; output3(v1);
33     std::cout << "v2: "; output3(v2);
34     v1.at(0).push_back(-1);
35     std::cout << "**********修改v1[0]后**********\n";
36     std::cout << "v1: \n"; output3(v1);
37     std::cout << "v2: \n"; output3(v2);
38 }
39 
40 void output1(const std::vector<int> &v) {
41     if(v.size() == 0) {std::cout << '\n'; return;}
42     std::cout << v.at(0);
43     for(auto i = 1; i < v.size(); ++i)
44         std::cout << ", " << v.at(i);
45     std::cout << '\n';
46 }
47 
48 void output2(const std::vector<int> &v) {
49     if(v.size() == 0) {std::cout << '\n'; return;}
50     auto it = v.begin();
51     std::cout << *it;
52     for(it = v.begin()+1; it != v.end(); ++it)
53         std::cout << ", " << *it;
54     std::cout << '\n';
55 }
56 
57 void output3(const std::vector<std::vector<int>>& v) {
58     if(v.size() == 0) {std::cout << '\n'; return;}
59     for(auto &i: v) output2(i);
60 }
task2.cpp

运行结果:

image

问题1:第一行调用 vector 的 “带大小和初值” 的构造函数,创建含 5 个元素的 vector,每个元素初值为 42,v1含 5 个 42。

    第二行调用 vector 的拷贝构造函数,用v1的值创建v2,v2同样含 5 个 42。

问题2:v1.size()=2;v2.size()=2;v1[0].size()=3。

问题3:能,at()会做越界检查,[ ]不做检查。

问题4:(1):能。v1.at(0)返回内层 vector 的引用,r绑定该引用,r.size()-1对应-1的索引,可输出。

      (2):优势:无需拷贝内层 vector,节省内存;限制:仅能读取r指向的内容,无法修改。

问题5:(1):深复制。修改v1(或嵌套 vector)不影响v2,两者数据独立。

      (2):非 constvector<int>的at()返回int&,constvector<int>的at()返回const int&;必须提供 const 重载,否则 const 对象无法调用at()读取元素。

实验任务三:

 1 #pragma once
 2 #include <iostream>
 3 #include <algorithm>
 4 
 5 class vectorInt {
 6 public:
 7     vectorInt();
 8     vectorInt(int n_);
 9     vectorInt(int n_, int value);
10     vectorInt(const vectorInt &vi);
11     ~vectorInt();
12     int size() const;
13     int& at(int index);
14     const int& at(int index) const;
15     vectorInt& assign(const vectorInt &vi);
16     int* begin();
17     int* end();
18     const int* begin() const;
19     const int* end() const;
20 
21 private:
22     int n;
23     int* ptr;
24 };
25 
26 vectorInt::vectorInt() : n{0}, ptr{nullptr} {}
27 vectorInt::vectorInt(int n_) : n{n_}, ptr{new int[n]} {}
28 vectorInt::vectorInt(int n_, int value) : n{n_}, ptr{new int[n_]} {
29     std::fill_n(ptr, n, value);
30 }
31 vectorInt::vectorInt(const vectorInt &vi) : n{vi.n}, ptr{new int[n]} {
32     std::copy_n(vi.ptr, vi.n, ptr);
33 }
34 vectorInt::~vectorInt() { delete[] ptr; }
35 
36 int vectorInt::size() const { 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     return const_cast<int&>(static_cast<const vectorInt*>(this)->at(index));
47 }
48 
49 vectorInt& vectorInt::assign(const vectorInt &vi) {
50     if (this == &vi) return *this;
51     int* ptr_tmp = new int[vi.n];
52     std::copy_n(vi.ptr, vi.n, ptr_tmp);
53     delete[] ptr;
54     n = vi.n;
55     ptr = ptr_tmp;
56     return *this;
57 }
58 
59 int* vectorInt::begin() { return ptr; }
60 int* vectorInt::end() { return ptr + n; }
61 const int* vectorInt::begin() const { return ptr; }
62 const int* vectorInt::end() const { return ptr + n; }
vectorInt.hpp
 1 #include "vectorInt.hpp"
 2 #include <iostream>
 3 
 4 void test1();
 5 void test2();
 6 void output1(const vectorInt &vi);
 7 void output2(const vectorInt &vi);
 8 
 9 int main() {
10     std::cout << "测试1: \n";
11     test1();
12     std::cout << "\n测试2: \n";
13     test2();
14 }
15 
16 void test1() {
17     int n;
18     std::cout << "Enter n: ";
19     std::cin >> n;
20     vectorInt x1(n);
21     for (auto i = 0; i < n; ++i) x1.at(i) = (i + 1) * 10;
22     std::cout << "x1: "; output1(x1);
23     
24     vectorInt x2(n, 42);
25     vectorInt x3(x2);
26     x2.at(0) = -1;
27     std::cout << "x2: "; output1(x2);
28     std::cout << "x3: "; output1(x3);
29 }
30 
31 void test2() {
32     const vectorInt x(5, 42);
33     vectorInt y;
34     y.assign(x);
35     std::cout << "x: "; output2(x);
36     std::cout << "y: "; output2(y);
37 }
38 
39 void output1(const vectorInt &vi) {
40     if (vi.size() == 0) { std::cout << '\n'; return; }
41     std::cout << vi.at(0);
42     for (auto i = 1; i < vi.size(); ++i)
43         std::cout << ", " << vi.at(i);
44     std::cout << '\n';
45 }
46 
47 void output2(const vectorInt &vi) {
48     if (vi.size() == 0) { std::cout << '\n'; return; }
49     auto it = vi.begin();
50     std::cout << *it;
51     for (it = vi.begin() + 1; it != vi.end(); ++it)
52         std::cout << ", " << *it;
53     std::cout << '\n';
54 }
task3.cpp

运行结果:

image

问题1:差异化代码:1.安全版本有自赋值检查:if (this == &vi) return *this;另一个无。当调用obj.assign(obj)(自赋值)时,版本 2 会直接执行delete[] ptr;,释放当前对象的内存。后续执行ptr = new int[n];时,虽然能分配新内存,但vi.ptr(即obj.ptr)已被释放,属于访问无效内存,会导致程序崩溃或数据乱码;而安全版本通过if (this == &vi)直接返回,完全规避该问题

                        2.版本1先分配新内存:int* ptr_tmp = new int[vi.n]; → 复制数据 → 再释放旧内存:delete[] ptr; 版本2先释放旧内存:delete[] ptr; → 再分配新内存:ptr = new int[n];先释放旧内存再分配新内存:若新内存分配失败,会造成内存泄漏,且 ptr 变为野指针;无临时内存缓冲:数据复制过程中出现异常会导致数据丢失,安全版本通过先分配临时内存、复制数据后释放旧内存,避免上述问题。

问题2:(1):这步转换将当前对象的指针(this,类型为 vectorInt*)强制转换为指向const对象的指针(类型为 const vectorInt*)。这里的目的就是为了复用 const 版本的 at() 方法中的核心逻辑(主要是越界检查),避免在非const版本中重复编写相同的代码。

          (2):这步转换将 const int& 类型(const版本at()的返回值)“去除const属性”,转换成 int& 类型。用const_cast来移除const限制,以满足非const版本at()的功能需求,同时又能复用const版本的代码。

问题3:(1):v1:int* begin();v2:const int* begin() const;对于非 const 对象(如vectorInt v1(5);):调对于非 const 对象(如vectorInt v1(5);):调用int* begin();。适配场景:外部需要修改元素(如*v1.begin() = 10;),返回的非 const 指针支持读写操作;对于 const 对象(如const vectorInt v2(5);):调用const int* begin() const;。适配场景:外部仅需读取元素(如std::cout << *v2.begin();),返回的 const 指针仅支持读操作,避免 const 对象被意外修改,符合 “const 正确性” 原则。

问题4:

    1.std::fill_n(ptr, n, value);

     功能:从ptr指向的内存开始,连续填充n个int类型元素,每个元素的值均为value;

   替代的手动循环:for(auto i = 0; i < n; ++i) ptr[i] = value;;

     优势:代码更简洁,避免手动循环的索引管理错误,且标准库算法经过优化,效率更高。

   2.std::copy_n(vi.ptr, vi.n, ptr);

   功能:从vi.ptr指向的内存开始,复制vi.n个int类型元素,将其依次存储到ptr指向的内存中;

   替代的手动循环:for(int i = 0; i < vi.n; ++i) ptr[i] = vi.ptr[i];;

   优势:自动处理元素复制,支持任意连续内存的拷贝,扩展性更强(如后续将int改为其他类型,无需修改循环逻辑)。

   3.std::copy_n(vi.ptr, vi.n, ptr_tmp);

   功能:与拷贝构造中的std::copy_n一致,从vi.ptr复制vi.n个元素到ptr_tmp(临时内存);

   作用:确保新内存中的数据与vi完全一致后,再替换旧内存的ptr,保证赋值过程中数据的完整性,同时避免手动循环的冗余代码。

实验任务四:

#pragma once
#include <iostream>
#include <cstdlib>

class Matrix {
public:
    Matrix(int rows_, int cols_, double value = 0) : n_rows(rows_), n_cols(cols_) {
        if (rows_ <= 0 || cols_ <= 0) {
            std::cerr << "Error: Invalid matrix size\n";
            exit(1);
        }
        ptr = new double[rows_ * cols_];
        for (int i = 0; i < rows_ * cols_; ++i) {
            ptr[i] = value;
        }
    }

    Matrix(int rows_, double value = 0) : n_rows(rows_), n_cols(rows_) {
        if (rows_ <= 0) {
            std::cerr << "Error: Invalid square matrix size\n";
            exit(1);
        }
        ptr = new double[rows_ * rows_];
        for (int i = 0; i < rows_ * rows_; ++i) {
            ptr[i] = value;
        }
    }

    Matrix(const Matrix &x) : n_rows(x.n_rows), n_cols(x.n_cols) {
        ptr = new double[n_rows * n_cols];
        for (int i = 0; i < n_rows * n_cols; ++i) {
            ptr[i] = x.ptr[i];
        }
    }

    ~Matrix() {
        delete[] ptr;
    }

    void set(const double *pvalue, int size) {
        if (size != n_rows * n_cols) {
            std::cerr << "Error: Size mismatch\n";
            exit(1);
        }
        for (int i = 0; i < size; ++i) {
            ptr[i] = pvalue[i];
        }
    }

    void clear() {
        for (int i = 0; i < n_rows * n_cols; ++i) {
            ptr[i] = 0.0;
        }
    }

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

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

    int rows() const { return n_rows; }

    int cols() const { return n_cols; }

    void 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 != n_cols - 1) std::cout << ", ";
            }
            std::cout << "\n";
        }
    }

private:
    int n_rows;
    int n_cols;
    double *ptr;
};
matrix.hpp
#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();
    return 0;
}

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.set(x, n*m);

    Matrix m2(m, n);
    m2.set(x, m*n);

    Matrix m3(n);
    m3.set(x, n*n);

    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);
}

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';
}
task4.cpp

运行结果:

image

实验任务五:

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

 

运行结果:

image

 

posted @ 2025-11-25 21:15  yahuao  阅读(0)  评论(0)    收藏  举报