C++学习系列(11):智能指针(unique_ptr、shared_ptr、weak_ptr)

C++学习系列(11):智能指针(unique_ptr、shared_ptr、weak_ptr)

1. 引言

在 C++ 传统的内存管理方式中,动态分配的对象需要手动释放,否则可能会导致 内存泄漏(Memory Leak)。为了解决这个问题,C++11 引入了 智能指针(Smart Pointer),它能 自动管理资源,避免内存泄漏。

本篇博客将介绍:

  • 智能指针的概念
  • 三种智能指针:unique_ptrshared_ptrweak_ptr
  • 智能指针的使用技巧
  • 如何避免智能指针的错误用法

2. 智能指针概述

智能指针是一个 包装类,用于管理动态分配的对象,并在 不再需要对象时自动释放内存

C++ 提供了三种智能指针:

智能指针 特点 适用场景
unique_ptr 独占所有权,不能共享 适用于 唯一所有权
shared_ptr 共享所有权,使用 引用计数 适用于 多个对象共享同一资源
weak_ptr 弱引用,不影响引用计数 适用于 避免循环引用

3. unique_ptr(独占所有权)

3.1 unique_ptr 的基本用法

#include 
#include 

class Foo {
public:
    Foo() { std::cout << "Foo 构造\n"; }
    ~Foo() { std::cout << "Foo 析构\n"; }
    void show() { std::cout << "Hello from Foo\n"; }
};

int main() {
    std::unique_ptr<Foo> ptr = std::make_unique<Foo>(); // 创建 unique_ptr
    ptr->show(); // 访问对象成员
    return 0; // 离开作用域时,Foo 自动销毁
}

解析

  • std::unique_ptr ptr = std::make_unique();推荐使用 make_unique
  • unique_ptr 不能被复制,只能 移动

3.2 unique_ptr 不能复制

std::unique_ptr<Foo> ptr1 = std::make_unique<Foo>();
std::unique_ptr<Foo> ptr2 = ptr1; // ❌ 错误:不能复制

3.3 unique_ptr 的所有权转移

std::unique_ptr<Foo> ptr1 = std::make_unique<Foo>();
std::unique_ptr<Foo> ptr2 = std::move(ptr1); // ✅ 允许移动

4. shared_ptr(共享所有权)

4.1 shared_ptr 的基本用法

#include 
#include 

class Foo {
public:
    Foo() { std::cout << "Foo 构造\n"; }
    ~Foo() { std::cout << "Foo 析构\n"; }
};

int main() {
    std::shared_ptr<Foo> ptr1 = std::make_shared<Foo>();
    std::shared_ptr<Foo> ptr2 = ptr1; // 共享同一对象

    std::cout << "引用计数: " << ptr1.use_count() << std::endl; // 2
    return 0; // 当最后一个 shared_ptr 离开作用域时,Foo 被销毁
}

解析

  • std::make_shared()推荐使用 make_shared
  • shared_ptr 可以被多个指针共享
  • 通过 use_count() 查看当前引用计数

4.2 shared_ptr 的引用计数

std::shared_ptr<int> sp1 = std::make_shared<int>(100);
std::shared_ptr<int> sp2 = sp1; // sp1 和 sp2 共享对象
std::cout << sp1.use_count() << std::endl; // 输出 2

5. weak_ptr(弱引用)

5.1 weak_ptr 的基本用法

#include 
#include 

class Foo {
public:
    Foo() { std::cout << "Foo 构造\n"; }
    ~Foo() { std::cout << "Foo 析构\n"; }
};

int main() {
    std::shared_ptr<Foo> sp = std::make_shared<Foo>();
    std::weak_ptr<Foo> wp = sp; // 创建 weak_ptr

    std::cout << "引用计数: " << sp.use_count() << std::endl; // 1

    if (auto locked = wp.lock()) { // 转换为 shared_ptr
        std::cout << "对象仍然存在\n";
    }
    return 0;
}

解析

  • weak_ptr 不会影响引用计数
  • 需要 lock() 转换为 shared_ptr 才能访问对象
  • 适用于 避免 shared_ptr 循环引用

5.2 避免 shared_ptr 循环引用

#include 
#include 

class B;

class A {
public:
    std::shared_ptr<B> bptr;
    ~A() { std::cout << "A 析构\n"; }
};

class B {
public:
    std::weak_ptr<A> aptr; // 解决循环引用
    ~B() { std::cout << "B 析构\n"; }
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();

    a->bptr = b;
    b->aptr = a; // 解决循环引用

    return 0;
}

解析

  • weak_ptr 不会增加引用计数,避免了 shared_ptr 循环引用导致的内存泄漏

6. 智能指针对比

智能指针 所有权 引用计数 是否可以共享 适用场景
unique_ptr 独占 单个对象,唯一所有权
shared_ptr 共享 多个对象共享同一资源
weak_ptr 弱引用 ✅(但不影响计数) 避免 shared_ptr 循环引用

7. 总结

unique_ptr 适用于 独占资源,不可复制,但可移动
shared_ptr 适用于 共享资源,引用计数自动管理
weak_ptr 适用于 避免 shared_ptr 循环引用


下一篇 C++学习系列(12):C++ 线程与多线程编程,敬请期待!

如果你喜欢这篇文章,欢迎点赞、收藏,并关注本系列!

你可能感兴趣的:(C++学习系列,c++,学习,java)