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

避免内存泄漏及泄漏后的排查方法【C++】

内存泄漏

  • 前言
  • 编码
    • 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 。


http://www.kler.cn/a/272261.html

相关文章:

  • adb 命令使用大全
  • Golang 中强大的重试机制,解决瞬态错误
  • AQS公平锁与非公平锁之源码解析
  • 游戏AI,让AI 玩游戏有什么作用?
  • VS Code AI开发之Copilot配置和使用详解
  • Java - WebSocket
  • Redis 常用数据类型,各自的使用场景是什么?
  • CentOS 7 编译安装 Git
  • AI基础知识(2)--决策树,神经网络
  • 编程语言的生态系统
  • 一种动态联动的实现方法
  • 使用gitee自动备份文件
  • 【C语言】指针基础知识(一)
  • 深度强化学习01
  • ubuntu18.04安装ffmpeg
  • OGRE Pittfals Design proposal for Ogre 2.0
  • Day67:WEB攻防-Java安全JNDIRMILDAP五大不安全组件RCE执行不出网
  • 代码随想录Day48:买卖股票的最佳时机、买卖股票的最佳时机II
  • 前端基础篇-深入了解 JavaScript(JSON、BOM、DOM 和事件监听)
  • C#学习路线指南
  • 云原生部署手册02:将本地应用部署至k8s集群
  • 数值分析复习:Newton插值
  • C/C++蓝桥杯之报数游戏
  • ASP.NET 服务器控件
  • Docker 安装 Skywalking以及UI界面
  • 数据库MySQL