当前位置: 首页 > news >正文

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++循环引用-CSDN博客

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 variablepd = shared_ptr<double>(p_reg); cout << "*pd = " << *pd << endl;// shared_ptr<double> pshared = p_reg; // wrong way, p_reg is not a shared_ptr variableshared_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 columnsMatrix(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 constructorMatrix(const Matrix& other) : rows_(other.rows_), cols_(other.cols_), data_(other.data_) {}// Copy assignment operatorMatrix& operator=(const Matrix& other) {if (this != &other) {rows_ = other.rows_;cols_ = other.cols_;data_ = other.data_;}return *this;}// Addition operatorMatrix 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 matrixvoid 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作为运算符重载检验。最后运行结果如下:


http://www.mrgr.cn/news/62504.html

相关文章:

  • Netron:神经网络模型可视化工具指南【全网最详细】
  • c加加11第一弹~
  • VMware USB服务导致主机USB设备加载驱动异常
  • 【C语言刷力扣】3216.交换后字典序最小的字符串
  • 【网络面试篇】TCP与UDP类
  • Java 数据类型、变量和操作符(2/30)
  • (五)Web前端开发进阶2——AJAX
  • Fsm3
  • Diving into the STM32 HAL-----USART
  • X86下fftw3库的编译和链接undefined reference to fftwf_malloc
  • 什么是ajax,为什么使用ajax?ajax都有哪些优点和缺点?
  • 006 单词倒序
  • 使用sql计算每天新增用户的ltv1、ltv2以及次留
  • 批量删除redis数据【亲测可用】
  • 校园社团信息管理:Spring Boot技术的优势与实现
  • 期货跟单、量化交易模拟演示系统
  • Abaqus自己构建材料库导入材料库
  • AUTOSAR CP中的CDD复杂驱动介绍
  • Javaweb梳理3——SQL概述+DDL语句1
  • SpringBoot旋律:打造现代Web音乐平台
  • 【专题】2024年金融数字化转型白皮书报告汇总PDF洞察(附原数据表)
  • 从“死敌”到“盟友”,英特尔和AMD世纪大“和解”!
  • 不再输入单号查快递,批量查快递单号信息的新方法,智能排序快递时效并查找时效相同的单号,一站式物流查询解决方案
  • 从零开始设计简易Queue:底层原理与实现
  • 【传知代码】检测图像P图痕迹(论文复现)
  • SpringBoot和弦:创建Web音乐网站指南