内存泄漏
- 前言
- 编码
- std::unique_ptr
- 申请单个对象
- 申请对象数组
- std::shared_ptr
- 申请单个对象
- 申请对象数组
- 编码总结
前言
最近在工作中被内存泄漏疯狂折磨,整理一下自己的思考。
编码
最近在工作中被内存泄漏疯狂折磨,我真的奉劝各位,如果你的业务代码中存在new,一定要把所有的new都干掉。已经2024年了,现在是很多程序员跟不上编译器适配新标准的节奏,而智能指针是在C++11上面就提供的特性。连 new 和 delete关键字都不要出现,因为一旦你的程序有内存泄漏,你就开始怀疑自己是不是 new 了没有 delete,这个过程非常折磨,而且肉眼去非常容易出错。我这里提供简单的实现,完全不去用 new 和 delete 关键字。
std::unique_ptr
std::unique_ptr
提供了对单个对象的独占所有权语义。
当 std::unique_ptr
离开作用域时,它所管理的对象会被自动销毁。
申请单个对象
#include <iostream>
#include <memory>
class MyClass
{
public:
MyClass(int x = 20) : value(x) { std::cout << "MyClass constructed\n"; }
~MyClass() { std::cout << "MyClass destroyed\n"; }
int value;
};
int main()
{
// 对于内置基本类型
std::unique_ptr<int> p1 = std::make_unique<int>(10); // 创建一个int对象,初始化为10
//备注:make_unique 在C++14才支持。
// 对于自定义类型
std::unique_ptr<MyClass> p2 = std::make_unique<MyClass>(20); // 创建一个MyClass对象
std::cout << *p1 << std::endl;
std::cout << p2->value << std::endl;
return 0;
}
运行结果:
申请对象数组
#include <iostream>
#include <memory>
class MyClass
{
public:
MyClass(int x = 20) : value(x) { std::cout << "MyClass constructed\n"; }
~MyClass() { std::cout << "MyClass destroyed\n"; }
int value;
};
int main()
{
// 对于内置基本类型
std::unique_ptr<int[]> arr1 = std::make_unique<int[]>(5); // 创建一个含有5个int的数组
// 对于自定义类型
std::unique_ptr<MyClass[]> arr2 = std::make_unique<MyClass[]>(5); // 创建一个含有5个MyClass对象的数组
for (int i = 0; i < 5; ++i)
{
std::cout << arr1[i] << " " << arr2[i].value << std::endl;
}
return 0;
}
运行结果:
std::shared_ptr
std::shared_ptr
提供了共享所有权的语义,多个 std::shared_ptr
可以同时拥有同一个对象的所有权。
当最后一个拥有某个对象的std::shared_ptr被销毁时,该对象将被自动删除。
申请单个对象
#include <iostream>
#include <memory>
class MyClass
{
public:
MyClass(int x = 20) : value(x) { std::cout << "MyClass constructed\n"; }
~MyClass() { std::cout << "MyClass destroyed\n"; }
int value;
};
int main()
{
// 对于内置基本类型
std::shared_ptr<int> p1 = std::make_shared<int>(10); // 创建一个int对象,初始化为10
// 对于自定义类型
std::shared_ptr<MyClass> p2 = std::make_shared<MyClass>(20); // 创建一个MyClass对象
std::cout << *p1 << std::endl;
std::cout << p2->value << std::endl;
return 0;
}
运行结果:
申请对象数组
std::shared_ptr
没有直接支持数组的语法糖像 std::unique_ptr
那样默认情况下,std::shared_ptr
会使用 delete
而不是 delete[]
来释放它所管理的资源,这对于动态分配的数组来说是不正确的。
如果你需要使用 std::shared_ptr
管理动态数组,需要提供自定义的删除器。(不建议使用)
#include <iostream>
#include <memory>
class MyClass
{
public:
MyClass(int x = 20) : value(x) { std::cout << "MyClass constructed\n"; }
~MyClass() { std::cout << "MyClass destroyed\n"; }
int value;
};
int main()
{
std::shared_ptr<int> arr(new int[5], std::default_delete<int[]>());
for (int i = 0; i < 5; ++i)
{
arr.get()[i] = i;
std::cout << arr.get()[i] << " ";
}
std::cout << std::endl;
// 创建一个std::shared_ptr来管理MyClass的一个数组
// 注意:使用自定义删除器来确保使用delete[]来释放数组
std::shared_ptr<MyClass> arr2(new MyClass[5],
[](MyClass* p) {delete[] p; });
for (int i = 0; i < 5; ++i)
{
std::cout << arr2.get()[i].value << " ";
}
std::cout << std::endl;
// 现在myArray智能指针负责管理这个动态分配的数组,当myArray离开作用域或被重置时,
// 自定义删除器会被调用来正确删除数组。
return 0;
}
运行结果:
我们提供另外一种方式 shared + vector:
#include <iostream>
#include <memory>
#include <vector>
int main() {
// 创建一个std::shared_ptr来管理一个std::vector<int>
auto sharedVector = std::make_shared<std::vector<int>>();
// 向vector中添加一些元素
sharedVector->push_back(10);
sharedVector->push_back(20);
sharedVector->push_back(30);
// 使用auto关键字遍历并打印vector的元素
std::cout << "Vector elements: ";
for (auto elem : *sharedVector) {
std::cout << elem << " ";
}
std::cout << std::endl;
// 创建一个新的shared_ptr实例,共享原有vector的所有权
std::shared_ptr<std::vector<int>> sharedVectorCopy = sharedVector;
// 通过共享的vector添加更多元素
sharedVectorCopy->push_back(40);
sharedVectorCopy->push_back(50);
// 再次打印vector的元素,展示更改
std::cout << "Vector elements after modification: ";
for (auto elem : *sharedVector) { // 注意:此处使用sharedVector或sharedVectorCopy访问的是同一个vector
std::cout << elem << " ";
}
std::cout << std::endl;
// 当sharedVector和sharedVectorCopy都离开作用域时,
// 它们管理的vector会被自动删除,不需要手动释放内存。
return 0;
}
运行结果:
编码总结
申请单个对象:std::shared_ptr ,std::unique_ptr 。
申请对象数组:std::shared_ptr + std::vector, std::unique_ptr 。