C++学习日记 | LAB 11 类中的动态内存管理

资料来源:南科大 于仕琪 C/C++ Program Design

LINK:

  1. CPP/week11 at main · ShiqiYu/CPP · GitHub

一、本节内容

       本节主要再次细致介绍 C++学习日记 | Lecture 11 类的动态内存管理 中的Hard copy、Soft copy以及智能指针的相关内容。

1.1 成员函数

1.1.1 Hard copy

         Hard copy让每个指针都单独指向自己的区域,同时在赋值时及时删除原先的内存区域。从而避免多次析构以及丢失创建时分配的内存。

Assignment operators typically combine the actions of the destructor and the copy constructor.

1.1.2 Soft copy

         Soft copy则是在类中新建一个变量存储有多少个指针指向了同一块区域,当没有指针指向它的时候才析构。

赋值构造函数被使用的四种情况:

1.2 智能指针

 

1.2.1 Unique pointer

unique_ptr

  • 这是一种独占所有权的智能指针,不允许拷贝构造,但支持移动语义 move。
  • 这意味着同一时间只能有一个unique_ptr指向给定的资源。
  • 这避免了多个指针指向同一块内存的情况,减少了内存泄漏的风险。

  • Is the assignment statement unique_ptr<int> up6 = up1;  OK? Why?
    • 不可以,语句 unique_ptr<int> up6 = up1; 在 C++ 中是不允许的。这是因为 unique_ptr 设计为独占对象的所有权,这意味着它不能被复制或赋值给另一个 unique_ptr。这样设计是为了确保在任何时候只有一个 unique_ptr 管理特定的资源,从而防止资源被多次删除等问题。

      如果需要将资源的所有权从 up1 转移到 up6,应该使用 std::move:

      • Is there any memory leak problem in the program? Need we use the statement delete[] p; to free the memory we allocated before?

      • float* p = new float[3]{1, 2, 3}; 这行代码分配了一个浮点数组,但后续并没有显式释放它。虽然将它传递给 unique_ptr<float[]> up5(p);,但在这种情况下,不需要显式调用 delete[] p;,因为 unique_ptr 会在其生命周期结束时自动释放内存。

        当执行 unique_ptr<float[]> up5(p); 时,up5 开始管理 p 指向的内存。这意味着 up5 会负责在其生命周期结束时释放这块内存。因此,不再需要也不应该手动调用 delete[] p;,因为 unique_ptr 会自动处理这个过程。

        并且,原有的 p 指针仍然存在,并且仍然可以通过 p 操控原有的内存。然而,这样做是不安全的,因为 unique_ptr 已经接管了该内存的所有权,并会在其生命周期结束时自动释放这块内存。如果在 unique_ptr 管理的内存上使用原始指针 p,可能会导致未定义行为。例如,如果 unique_ptr 释放了内存,而仍然通过 p 访问这块内存,就会导致悬空指针问题。为了避免这种情况,建议在将所有权转移给 unique_ptr 后,不再使用原始指针 p。这样可以确保内存管理的安全性和一致性。

        关于指针的排他性,unique_ptr 确实是独占所有权的智能指针,但是这只是意味着它不允许另一个 unique_ptr 共享同一块内存,不意味着原始指针 p 会被自动销毁或失效。

 1.2.1 Shared pointer

shared_ptr

  • 这是一种引用计数智能指针,它允许多个指针拥有同一个对象
  • 当最后一个拥有该对象的shared_ptr被销毁时,它会自动释放所指向的内存。
  • 使用std::make_shared可以高效地创建一个shared_ptr实例。

        shared pointer并不会总是可以自动释放内存,当存在循环引用的情况时,其无法自动释放内存,需要weak_ptr配合处理。 

ref.深入理解C++中的循环引用问题及解决方法_c++循环引用-优快云博客

weak_ptr

  • 它是一种与shared_ptr配合使用的智能指针,主要用于解决shared_ptr循环引用的问题。
  • weak_ptr不会增加引用计数,因此可以用来打破潜在的循环引用,防止内存无法释放。
  • 在访问所引用的对象前必须先转换为 std::shared_ptr。
  • std::weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。

二、习题笔记 

习题1 

        上述代码会报错。

修改后的代码:

#include <iostream>
#include <memory>
using namespace std;
int main()
{
    double *p_reg = new double(5);
    shared_ptr<double> pd;
    // pd = p_reg;    // wrong way, p_reg is not a shared_ptr variable
    pd = shared_ptr<double>(p_reg); 
    cout << "*pd = " << *pd << endl;
    // shared_ptr<double> pshared = p_reg; // wrong way, p_reg is not a shared_ptr variable
    shared_ptr<double> pshared(p_reg); 
    cout << "*pshred = " << *pshared << endl;
    string str("Hello World!");
    shared_ptr<string> pstr(&str); 
    cout << "*pstr = " << *pstr << endl;
    return 0;
}

运行结果:

习题2

#include <iostream>
#include <memory>
#include <vector>
class Matrix {
private:
    int rows_, cols_;
    std::shared_ptr<float[]> data_;
public:
    // Constructor to initialize matrix with given rows and columns
    Matrix(int rows, int cols) : rows_(rows), cols_(cols), data_(new float[rows * cols], std::default_delete<float[]>()) {
        std::fill(data_.get(), data_.get() + rows * cols, 1.0f);
    }
    // data_.get() 返回指向动态分配的数组的原始指针。
    // data_.get() + rows * cols 计算数组的结束位置。
    // std::fill 函数将数组的所有元素初始化为 1.0f。


    // Copy constructor
    Matrix(const Matrix& other) : rows_(other.rows_), cols_(other.cols_), data_(other.data_) {}
    // Copy assignment operator
    Matrix& operator=(const Matrix& other) {
        if (this != &other) {
            rows_ = other.rows_;
            cols_ = other.cols_;
            data_ = other.data_;
        }
        return *this;
    }
    // Addition operator
    Matrix operator+(const Matrix& other) const {
        if (rows_ != other.rows_ || cols_ != other.cols_) {
            throw std::invalid_argument("Matrices dimensions must match for addition.");
        }
        Matrix result(rows_, cols_);
        for (int i = 0; i < rows_ * cols_; ++i) {
            result.data_.get()[i] = data_.get()[i] + other.data_.get()[i];
        }
        return result;
    }
    // Function to print the matrix
    void print() const {
        for (int i = 0; i < rows_; ++i) {
            for (int j = 0; j < cols_; ++j) {
                std::cout << data_.get()[i * cols_ + j] << " ";
            }
            std::cout << std::endl;
        }
    }
};
int main() {
    Matrix a(3, 4);
    Matrix b(3, 4);
    std::cout << "Matrix a:" << std::endl;
    a.print();
    std::cout << "Matrix b:" << std::endl;
    b.print();
    
    Matrix c = a + b;
    std::cout << "Matrix c:" << std::endl;
    c.print();
    Matrix d = a;
    std::cout << "Matrix d:" << std::endl;
    d.print();
    d = c;
    std::cout << "Matrix d:" << std::endl;
    d.print();
    return 0;
}

        为了方便验证效果,初始化时将数组初始化为全1,同时最后使用d=c作为运算符重载检验。最后运行结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电子异术家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值