C++学习日记 | LAB 11 类中的动态内存管理
资料来源:南科大 于仕琪 C/C++ Program Design
LINK:
- 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作为运算符重载检验。最后运行结果如下: