常见的 C++ 内存泄漏场景,智能指针解决的示例

「C++ 40 周年」主题征文大赛(有机会与C++之父现场交流!) 10w+人浏览 708人参与

告别 C++ 内存泄漏!智能指针实战指南(附完整示例)

谁没被 C++ 内存泄漏坑过?隐蔽难查、耗尽资源、程序崩溃,这些痛点让无数开发者头疼不已!传统手动管理内存(new/delete)稍不留意就会踩坑 —— 忘记释放、异常中断导致释放失效、指针悬空等场景屡见不鲜。而智能指针作为 C++ 的 “内存管理救星”,能自动完成资源回收,从根源上规避泄漏风险。本文不仅拆解高频泄漏场景,更附上可直接复用的智能指针实战代码,帮你快速掌握无泄漏编程技巧!

我遇到了一个 C++ 内存泄漏问题,请帮我分析常见的 C++ 内存泄漏场景,并提供一个使用智能指针解决的示例。

我来帮你分析C++内存泄漏问题并提供解决方案。

一、常见的C++内存泄漏场景

1. 忘记释放动态分配的内存

cpp

void memoryLeakExample1() {
    int* ptr = new int(42);
    // 忘记 delete ptr;
    return; // 内存泄漏!
}

2. 异常导致的内存泄漏

cpp

void memoryLeakExample2() {
    int* ptr = new int(42);
    throw std::runtime_error("异常发生");
    delete ptr; // 这行永远不会执行
}

3. 数组内存泄漏

cpp

void memoryLeakExample3() {
    int* arr = new int[100];
    // 忘记使用 delete[] arr;
    return; // 内存泄漏!
}

4. 循环引用(使用shared_ptr时)

cpp

class Node {
public:
    std::shared_ptr<Node> next;
    std::shared_ptr<Node> prev;
    int data;
};
// 可能导致循环引用,两个节点互相引用时无法自动释放

二、使用智能指针解决内存泄漏

完整示例:智能指针解决方案

cpp

#include <iostream>
#include <memory>
#include <vector>
#include <stdexcept>

// 示例类:学生信息
class Student {
private:
    std::string name;
    int age;
    std::vector<int> scores;

public:
    Student(const std::string& n, int a) : name(n), age(a) {
        std::cout << "Student " << name << " created\n";
    }
    
    ~Student() {
        std::cout << "Student " << name << " destroyed\n";
    }
    
    void addScore(int score) {
        scores.push_back(score);
    }
    
    void printInfo() const {
        std::cout << "Name: " << name << ", Age: " << age << std::endl;
    }
};

// 1. 使用 unique_ptr 解决基本内存管理问题
class StudentManager {
private:
    std::vector<std::unique_ptr<Student>> students;

public:
    // 添加学生 - 使用 unique_ptr 自动管理内存
    void addStudent(const std::string& name, int age) {
        students.push_back(std::make_unique<Student>(name, age));
    }
    
    // 获取学生引用
    Student& getStudent(size_t index) {
        if (index >= students.size()) {
            throw std::out_of_range("Index out of range");
        }
        return *students[index];
    }
    
    // 显示所有学生信息
    void printAllStudents() const {
        for (const auto& student : students) {
            student->printInfo();
        }
    }
};

// 2. 使用 shared_ptr 和 weak_ptr 解决循环引用问题
class TreeNode {
public:
    int value;
    std::shared_ptr<TreeNode> left;
    std::shared_ptr<TreeNode> right;
    std::weak_ptr<TreeNode> parent; // 使用 weak_ptr 避免循环引用
    
    TreeNode(int val) : value(val) {
        std::cout << "TreeNode " << value << " created\n";
    }
    
    ~TreeNode() {
        std::cout << "TreeNode " << value << " destroyed\n";
    }
    
    // 设置左子节点
    void setLeft(std::shared_ptr<TreeNode> node) {
        left = node;
        if (node) {
            node->parent = std::weak_ptr<TreeNode>(shared_from_this());
        }
    }
    
    // 设置右子节点
    void setRight(std::shared_ptr<TreeNode> node) {
        right = node;
        if (node) {
            node->parent = std::weak_ptr<TreeNode>(shared_from_this());
        }
    }
    
    // 检查是否有父节点
    bool hasParent() const {
        return !parent.expired();
    }
};

// 3. 异常安全的内存管理示例
class SafeResource {
private:
    std::unique_ptr<int[]> data;
    size_t size;

public:
    SafeResource(size_t n) : size(n) {
        data = std::make_unique<int[]>(size);
        std::cout << "SafeResource of size " << size << " created\n";
        
        // 模拟可能抛出异常的操作
        if (n > 1000) {
            throw std::runtime_error("Size too large");
        }
        
        // 初始化数据
        for (size_t i = 0; i < size; ++i) {
            data[i] = static_cast<int>(i);
        }
    }
    
    ~SafeResource() {
        std::cout << "SafeResource destroyed\n";
    }
    
    int& operator[](size_t index) {
        if (index >= size) {
            throw std::out_of_range("Index out of range");
        }
        return data[index];
    }
    
    size_t getSize() const { return size; }
};

// 4. 演示智能指针的优势
void demonstrateSmartPointers() {
    std::cout << "=== 智能指针演示 ===\n";
    
    try {
        // 使用 unique_ptr - 自动释放内存
        {
            std::cout << "\n1. unique_ptr 演示:\n";
            auto studentManager = std::make_unique<StudentManager>();
            studentManager->addStudent("Alice", 20);
            studentManager->addStudent("Bob", 22);
            studentManager->printAllStudents();
        } // studentManager 自动销毁,所有Student也自动销毁
        
        // 使用 shared_ptr - 共享所有权
        {
            std::cout << "\n2. shared_ptr 演示:\n";
            auto root = std::make_shared<TreeNode>(1);
            auto left = std::make_shared<TreeNode>(2);
            auto right = std::make_shared<TreeNode>(3);
            
            root->setLeft(left);
            root->setRight(right);
            
            std::cout << "Left child has parent: " << left->hasParent() << std::endl;
        } // 所有节点自动正确释放,无循环引用问题
        
        // 异常安全演示
        {
            std::cout << "\n3. 异常安全演示:\n";
            auto resource = std::make_unique<SafeResource>(10);
            std::cout << "Resource[5] = " << (*resource)[5] << std::endl;
        }
        
    } catch (const std::exception& e) {
        std::cout << "Exception caught: " << e.what() << std::endl;
        // 即使发生异常,智能指针也会自动清理资源
    }
}

// 5. 对比:传统方式 vs 智能指针方式
void compareApproaches() {
    std::cout << "\n=== 传统方式 vs 智能指针方式对比 ===\n";
    
    // 传统方式 - 容易出错
    std::cout << "\n传统方式(容易出错):\n";
    Student* traditionalStudent = new Student("Traditional", 25);
    // 需要手动 delete traditionalStudent;
    // 如果忘记或发生异常,就会内存泄漏
    
    // 智能指针方式 - 自动管理
    std::cout << "\n智能指针方式(自动管理):\n";
    auto smartStudent = std::make_unique<Student>("Smart", 25);
    // 自动释放,无需手动管理
    
    // 手动释放传统方式的内存
    delete traditionalStudent;
}

int main() {
    try {
        demonstrateSmartPointers();
        compareApproaches();
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    
    return 0;
}

三、智能指针使用最佳实践

1. 选择合适的智能指针类型

cpp

// 独占所有权 - 使用 unique_ptr
std::unique_ptr<Student> createStudent(const std::string& name, int age) {
    return std::make_unique<Student>(name, age);
}

// 共享所有权 - 使用 shared_ptr
class ResourceManager {
private:
    std::shared_ptr<SafeResource> resource;
    
public:
    void setResource(std::shared_ptr<SafeResource> res) {
        resource = res;
    }
    
    std::shared_ptr<SafeResource> getResource() {
        return resource;
    }
};

// 避免循环引用 - 使用 weak_ptr
class Parent {
public:
    std::vector<std::shared_ptr<TreeNode>> children;
    
    void addChild(std::shared_ptr<TreeNode> child) {
        children.push_back(child);
        child->parent = std::weak_ptr<TreeNode>(shared_from_this());
    }
};

2. 内存泄漏检测工具推荐

cpp

// 编译时检查内存泄漏的简单方法
#ifdef DEBUG
    #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
    // Replace _NORMAL_BLOCK with the correct block type.
    #define new DBG_NEW
#endif

四、总结

智能指针的主要优势:

  1. 自动内存管理:无需手动delete
  2. 异常安全:即使发生异常也能正确释放内存
  3. 类型安全:编译时检查
  4. 明确所有权:unique_ptr表示独占所有权,shared_ptr表示共享所有权

使用建议:

  • 优先使用 unique_ptr
  • 需要共享所有权时使用 shared_ptr
  • 避免循环引用时使用 weak_ptr
  • 多使用 make_uniquemake_shared 工厂函数

这样可以有效避免大部分内存泄漏问题。

💡注意:本文所介绍的软件及功能均基于公开信息整理,仅供用户参考。在使用任何软件时,请务必遵守相关法律法规及软件使用协议。同时,本文不涉及任何商业推广或引流行为,仅为用户提供一个了解和使用该工具的渠道。

你在生活中时遇到了哪些问题?你是如何解决的?欢迎在评论区分享你的经验和心得!

希望这篇文章能够满足您的需求,如果您有任何修改意见或需要进一步的帮助,请随时告诉我!

感谢各位支持,可以关注我的个人主页,找到你所需要的宝贝。 ​ 
博文入口:https://blog.youkuaiyun.com/Start_mswin ​复制到【浏览器】打开即可,宝贝入口:https://pan.quark.cn/s/b42958e1c3c0

作者郑重声明,本文内容为本人原创文章,纯净无利益纠葛,如有不妥之处,请及时联系修改或删除。诚邀各位读者秉持理性态度交流,共筑和谐讨论氛围~

#C++ #C++40周年

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

山峰哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值