深入学习C++:智能指针的应用

善良的小乔

智能指针的应用

一. 基本概念

智能指针是 C++ 中用于管理动态内存的强大工具,旨在避免手动内存管理带来的内存泄漏和悬空指针问题。它们通过 RAII(资源获取即初始化)技术自动释放不再使用的内存。

核心概念

1. RAII(Resource Acquisition Is Initialization)
资源在对象构造时获取,在对象析构时释放。智能指针通过这一原则,将内存管理与对象生命周期绑定。

2. 所有权
智能指针通过控制对象的所有权来决定何时释放内存:

(1):独占所有权:同一时间只有一个指针可访问对象(如 std::unique_ptr)。

(2):共享所有权:多个指针可共享对象,使用引用计数跟踪引用数(如 std::shared_ptr)。

二. 智能指针的优点 

1. 避免内存泄漏

问题:手动管理内存时,若忘记调用 delete(如异常抛出或提前返回),会导致内存泄漏。

智能指针解决方案:对象生命周期结束时(如离开作用域),智能指针自动释放内存。

void manual_memory_leak() {
    int* ptr = new int(42);
    if (condition) return; // 提前返回,内存未释放
    delete ptr; // 不会执行
}

void smart_ptr_safe() {
    std::unique_ptr<int> ptr = std::make_unique<int>(42);
    if (condition) return; // ptr 自动释放内存
} // 正常退出时,ptr 也会释放内存

2. 防止悬空指针

问题:多个指针指向同一对象,其中一个删除对象后,其他指针变为悬空指针。

智能指针解决方案

1. std::unique_ptr 禁止拷贝,确保单一所有权。

2. std::shared_ptr 使用引用计数,所有指针销毁后才释放对象。

void dangling_ptr() {
    int* ptr1 = new int(42);
    int* ptr2 = ptr1;
    delete ptr1;
    *ptr2; // 悬空指针!
}

void smart_ptr_safe() {
    std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
    std::shared_ptr<int> ptr2 = ptr1; // 引用计数+1
    ptr1.reset(); // 引用计数-1,对象仍存在
    *ptr2; // 安全!
} // ptr2 销毁时,引用计数为0,对象被删除

3. 解决循环引用问题 

问题:两个 shared_ptr 相互引用会导致引用计数永远不为 0,造成内存泄漏。

智能指针解决方案:使用 std::weak_ptr 打破循环引用。weak_ptr 不增加引用计数,仅观察对象。

struct Parent;
struct Child {
    std::shared_ptr<Parent> parent; // 正常使用 shared_ptr
    ~Child() { std::cout << "Child destroyed" << std::endl; }
};

struct Parent {
    std::weak_ptr<Child> child; // 使用 weak_ptr 避免循环引用
    ~Parent() { std::cout << "Parent destroyed" << std::endl; }
};

void circular_reference() {
    auto parent = std::make_shared<Parent>();
    auto child = std::make_shared<Child>();
    parent->child = child; // weak_ptr 不增加引用计数
    child->parent = parent; // shared_ptr 正常赋值
} // parent 和 child 均正常释放

三. C++ 标准库中的智能指针 

C++ 提供了三种主要的智能指针,位于 <memory> 头文件中:

1. std::unique_ptr

特性:独占所有权,同一时间只能有一个指针指向对象。

用途:管理不共享的资源。

示例:

#include <memory>

void example() {
    // 创建 unique_ptr
    std::unique_ptr<int> ptr = std::make_unique<int>(42); // C++14
    // 或使用 new(不推荐):std::unique_ptr<int> ptr(new int(42));

    // 访问对象
    *ptr = 100;

    // 转移所有权(使用 std::move)
    std::unique_ptr<int> ptr2 = std::move(ptr); // ptr 现在为空
    
    // 不能复制 unique_ptr
    // std::unique_ptr<int> ptr3 = ptr2; // 错误!
} // ptr2 离开作用域,自动释放内存

2. std::shared_ptr 

特性:共享所有权,使用引用计数跟踪有多少指针指向同一对象。

用途:多个指针需要共享同一资源时。

示例:

#include <memory>
#include <iostream>

struct MyClass {
    ~MyClass() { std::cout << "Destroying MyClass" << std::endl; }
};

void example() {
    // 创建 shared_ptr
    std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>(); // 引用计数 = 1

    {
        std::shared_ptr<MyClass> ptr2 = ptr1; // 引用计数 = 2
        std::cout << "Inside scope: " << ptr1.use_count() << std::endl; // 输出 2
    } // ptr2 销毁,引用计数 = 1

    std::cout << "Outside scope: " << ptr1.use_count() << std::endl; // 输出 1
} // ptr1 销毁,引用计数 = 0,对象被删除

3. std::weak_ptr 

特性:弱引用,不控制对象生命周期,解决 shared_ptr 的循环引用问题。

用途:观察对象但不拥有它,避免循环引用导致的内存泄漏。

示例:

#include <memory>

struct B; // 前向声明

struct A {
    std::weak_ptr<B> b_ptr; // 使用 weak_ptr 避免循环引用
    ~A() { std::cout << "A destroyed" << std::endl; }
};

struct B {
    std::shared_ptr<A> a_ptr; // 正常使用 shared_ptr
    ~B() { std::cout << "B destroyed" << std::endl; }
};

void example() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    a->b_ptr = b; // weak_ptr 可直接赋值
    b->a_ptr = a; // shared_ptr 正常赋值

    // 使用 weak_ptr 前需检查对象是否存在
    if (auto shared_b = a->b_ptr.lock()) { // 创建临时 shared_ptr
        // 使用 shared_b 访问 B 对象
    }
} // a 和 b 都能正常释放,无内存泄漏

四. 最佳实践

1. 优先使用 std::unique_ptr:当资源不需要共享时。

2. 使用 std::make_unique 和 std::make_shared:更安全地创建智能指针(异常安全)。

3. 避免混合使用智能指针和原始指针:

int* raw = new int(42);
std::shared_ptr<int> ptr1(raw); // 危险!
std::shared_ptr<int> ptr2(raw); // 错误!多个独立的 shared_ptr 管理同一内存

4. 使用std::weak_ptr打破循环引用

5. 避免从 this 创建 shared_ptr:使用 std::enable_shared_from_this 安全获取 shared_ptr

五. 总结 

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值