AI 点评
cover

智能指针

Fool Lv.6 2026-06-24 11:08 1723 字 浏览 87 点赞 评论 0 学习C++

#include <memory>

原生指针

原生指针是 C++ 最基本的指针类型,允许程序员直接管理内存。然而,原生指针存在以下问题:

  • 内存泄漏:未释放动态分配的内存。
  • 悬挂指针:指针指向已释放或未初始化的内存。
  • 双重释放:多次释放同一内存区域。

智能指针的优势

智能指针通过封装原生指针,自动管理内存,解决上述问题。主要优势包括:

  • 自动销毁:在智能指针生命周期结束时自动释放资源。
  • 引用计数:共享智能指针能够跟踪引用数量,确保资源在最后一个引用结束时释放。
  • 避免内存泄漏:通过 RAII 机制自动管理资源生命周期。
  • 类型安全:提供更严格的类型检查,减少错误。

std::unique_ptr

定义与用法

<font style="background-color:rgb(247, 247, 249);">std::unique_ptr</font> 是一种独占所有权的智能指针,任何时刻只能有一个 <font style="background-color:rgb(247, 247, 249);">unique_ptr</font> 实例拥有对某个对象的所有权。不能被拷贝,只能被移动。

主要特性:

  • 独占所有权:确保资源在一个所有者下。
  • 轻量级:没有引用计数,开销小。
  • 自动释放:在指针销毁时自动释放资源。

构造函数与赋值

<font style="background-color:rgb(247, 247, 249);">unique_ptr</font> 提供多种构造函数和赋值操作,以支持不同的使用场景。

  • 默认构造函数:创建一个空的 <font style="background-color:rgb(247, 247, 249);">unique_ptr</font>。
  • 指针构造函数:接受一个裸指针,拥有其所有权。
  • 移动构造函数:将一个 <font style="background-color:rgb(247, 247, 249);">unique_ptr</font> 的所有权转移到另一个 <font style="background-color:rgb(247, 247, 249);">unique_ptr</font>。
  • 移动赋值操作符:将一个 <font style="background-color:rgb(247, 247, 249);">unique_ptr</font> 的所有权转移到另一个 <font style="background-color:rgb(247, 247, 249);">unique_ptr</font>。

移动语义

由于 <font style="background-color:rgb(247, 247, 249);">unique_ptr</font> 不能被拷贝,必须通过移动语义转移所有权。这保证了资源的独占性。

std::shared_ptr

定义与用法

<font style="background-color:rgb(247, 247, 249);">std::shared_ptr</font> 是一种共享所有权的智能指针,允许多个 <font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 实例共享对同一个对象的所有权。通过引用计数机制,管理资源的生命周期。

主要特性:

  • 共享所有权:多个 <font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 可以指向同一个对象。
  • 引用计数:跟踪有多少 <font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 实例指向同一对象。
  • 自动释放:当引用计数为0时,自动释放资源。

引用计数与控制块

<font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 背后依赖一个控制块(Control Block),用于存储引用计数和指向实际对象的指针。控制块的主要内容包括:

  • 强引用计数(**<font style="background-color:rgb(247, 247, 249);">use_count</font>**):表示有多少个 <font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 指向对象。
  • 弱引用计数(**<font style="background-color:rgb(247, 247, 249);">weak_count</font>**):表示有多少个 <font style="background-color:rgb(247, 247, 249);">weak_ptr</font> 指向对象(不增加强引用计数)。

构造函数与赋值

<font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 提供多种构造函数和赋值操作,以支持不同的使用场景。

  • 默认构造函数:创建一个空的 <font style="background-color:rgb(247, 247, 249);">shared_ptr</font>。
  • 指针构造函数:接受一个裸指针,拥有其所有权。
  • 拷贝构造函数:增加引用计数,共享对象所有权。
  • 移动构造函数:转移所有权,源 <font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 变为空。
  • 拷贝赋值操作符:释放当前资源,增加引用计数,指向新对象。
  • 移动赋值操作符:释放当前资源,转移所有权,源 <font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 变为空。

std::weak_ptr

定义与用法

<font style="background-color:rgb(247, 247, 249);">std::weak_ptr</font> 是一种不拥有对象所有权的智能指针,用于观察但不影响对象的生命周期。主要用于解决 <font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 之间的循环引用问题。

主要特性:

  • 非拥有所有权:不增加引用计数。
  • 可从** **<font style="background-color:rgb(247, 247, 249);">shared_ptr</font>** **生成:通过 <font style="background-color:rgb(247, 247, 249);">std::weak_ptr</font> 可以访问 <font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 管理的对象。
  • 避免循环引用:适用于双向关联或观察者模式。

避免循环引用

在存在双向关联(如父子关系)时,使用多个 <font style="background-color:rgb(247, 247, 249);">shared_ptr</font> 可能导致循环引用,导致内存泄漏。此时,可以使用 <font style="background-color:rgb(247, 247, 249);">weak_ptr</font> 来打破循环。

  • wp.lock() 返回一个 shared_ptr,如果对象依然存在,则有效。
  • sp.reset() 释放资源后,wp.lock() 无法获取有效的 shared_ptr。

自定义删除器

// 封装类实现删除
struct FileDeleter {
    void operator()(FILE* fp) const {
        if (fp) {
            std::cout << "Closing file." << std::endl;
            fclose(fp);
        }
    }
};

// lambda表达式
 auto fileDeleter = [](FILE* fp) {
    if (fp) {
        std::cout << "Closing file via lambda." << std::endl;
        fclose(fp);
    }
};

// 使用
if (filePtr) {
    std::cout << "File opened successfully." << std::endl;
    fprintf(filePtr.get(), "Hello, Lambda!\n");
}

最佳实践与常见陷阱

选择合适的智能指针

  • std::unique_ptr:
    • 用于明确的独占所有权场景。
    • 适用于资源的单一管理者或需要所有权转移的情况。
    • 更轻量,性能更优。
  • std::shared_ptr:
    • 用于共享所有权的场景。
    • 需要多个指针共同管理同一资源时使用。
    • 引用计数带来一定的性能开销。
  • std::weak_ptr:
    • 用于观察不拥有资源的场景。
    • 适用于需要避免循环引用或只需临时访问资源的情况。

避免循环引用

在使用 shared_ptr 时,特别是在对象间存在双向引用时,容易导致循环引用,内存泄漏。使用 weak_ptr 打破循环引用。

使用 make_shared 与 make_unique

优先使用 make_shared 和 make_unique 来创建智能指针,避免直接使用 new,提高效率和异常安全性。

auto sp = std::make_shared<Test>(100);
auto up = std::make_unique<Test>(200);

不要混用原生指针与智能指针

避免在智能指针管理的对象上同时使用原生指针进行管理,防止重复释放或不安全访问。

理解智能指针的所有权语义

深入理解不同智能指针的所有权规则,避免误用导致资源管理错误。

最后更新时间:2026-10-11 20:43

微信扫码分享

评论(0) 最新 热门

登录 后才能评论。