C++面试6_智能指针

C++ 的智能指针是用来自动管理动态内存(避免内存泄漏和悬空指针)的一类模板类,主要定义在 <memory> 头文件中。
它们本质上是对原始指针(raw pointer)的封装类模板,通过引用计数或所有权机制来自动释放资源。

🧩 一、智能指针的主要种类

智能指针所属头文件所有权模型主要特点
std::auto_ptr (C++98, 已废弃)<memory>独占所有权(转移语义不安全)已在 C++11 废弃,C++17 删除,不推荐使用
std::unique_ptr<memory>独占所有权(唯一持有)无引用计数,不能复制,只能移动
std::shared_ptr<memory>共享所有权(引用计数)支持多个指针共享同一资源,计数为 0 时自动释放
std::weak_ptr<memory>弱引用(不计入引用计数)shared_ptr 配合使用,解决循环引用问题

🧠 二、各类智能指针详细区别

1. std::unique_ptr —— 独占所有权指针

  • 核心特征:

    • 同一时间内只能有一个 unique_ptr 拥有资源。

    • 不能被复制(删除了拷贝构造和赋值函数)。

    • 可以通过 std::move 转移所有权。

  • 适用场景:

    • 对象在某个作用域中唯一存在。

    • 避免动态分配内存后忘记 delete

    • 示例:

    • #include <memory>
      #include <iostream>

      struct Test { 
          Test() { std::cout << "Construct\n"; }
          ~Test() { std::cout << "Destruct\n"; }
      };

      int main() {
          std::unique_ptr<Test> p1 = std::make_unique<Test>();
          // std::unique_ptr<Test> p2 = p1;  // ❌ 不可复制
          std::unique_ptr<Test> p2 = std::move(p1); // ✅ 转移所有权
      }

2. std::shared_ptr —— 共享所有权指针

  • 核心特征:

    • 多个 shared_ptr 可共享同一资源。

    • 内部维护一个引用计数器(use_count)

    • 当最后一个 shared_ptr 被销毁时,资源自动释放。

  • 适用场景:

    • 多个对象共享同一个动态资源。

3. std::weak_ptr —— 弱引用指针

  • 核心特征:

    • 不增加引用计数

    • 用于解决 shared_ptr 循环引用问题。

    • 不能直接解引用,必须通过 lock() 获取临时的 shared_ptr

  • 适用场景:

    • 观察者模式(Observer Pattern)。

    • 避免循环引用。

  • 循环引用问题示例:

  • #include <memory>

  • struct B;  // 前向声明

    struct A {
        std::shared_ptr<B> b_ptr;
    };
    struct B {
        std::shared_ptr<A> a_ptr;  // ❌ 互相持有 shared_ptr,会造成循环引用
    };

    // 改进版
    struct B_fixed {
        std::weak_ptr<A> a_ptr;   // ✅ 弱引用,不增加计数
    };

    4. std::auto_ptr(已废弃)

  • C++98 中引入,但因为:

    • 拷贝操作会转移所有权;

    • 无法与标准容器、安全语义兼容;

    在 C++11 中被 unique_ptr 替代。

  • ⚖️ 三、区别总结表

特性unique_ptrshared_ptrweak_ptrauto_ptr(废弃)
所有权独占共享弱引用独占(复制转移)
引用计数
拷贝✅(仅指向)✅(转移所有权)
移动
循环引用问题
推荐使用✅(辅助)

四、使用建议

  1. 首选 std::unique_ptr

    • 若资源只有唯一所有者;

    • 性能最佳,无引用计数开销。

  2. 使用 std::shared_ptr

    • 若资源需要被多个对象共享;

    • 注意循环引用。

  3. 配合 std::weak_ptr

    • 用于解决 shared_ptr 的循环依赖;

    • 用于“观察者”或缓存等场景。

🧩 一、基础题

1. 什么是智能指针?为什么要使用智能指针?

答:
智能指针是对原始指针(raw pointer)的封装,用于自动管理动态内存的生命周期
当智能指针离开作用域时,会自动调用 delete 释放资源,避免:

  • 内存泄漏;

  • 悬空指针;

  • 重复释放等问题。

2. C++ 中有哪些智能指针?区别是什么?

智能指针所有权引用计数特点
unique_ptr独占不可复制,只能转移(std::move
shared_ptr共享多个指针共享资源,引用计数为0时释放
weak_ptr弱引用不增加计数,防止循环引用
auto_ptr(废弃)独占拷贝时转移所有权,不安全,已被废弃

3. unique_ptr 可以被复制吗

不能。
unique_ptr 的拷贝构造函数和赋值操作符被 删除
如果想要转移所有权,可以使用 std::move()

std::unique_ptr<int> p1 = std::make_unique<int>(5);
// std::unique_ptr<int> p2 = p1; ❌ 编译错误
std::unique_ptr<int> p2 = std::move(p1); ✅ // 所有权转移

4. shared_ptr 的引用计数是如何实现的?

答:
shared_ptr 通过一个**控制块(control block)**管理:

  • 保存指向对象的指针;

  • 保存引用计数弱引用计数

当:

  • 新的 shared_ptr 指向相同对象 → 计数 +1

  • 一个 shared_ptr 析构 → 计数 -1
    当计数归零时,自动调用 delete 释放内存。

  • 5. 为什么要有 weak_ptr

答:
为了解决 shared_ptr 的**循环引用(circular reference)**问题。
如果两个对象互相持有 shared_ptr,计数永远不会为 0,导致内存泄漏。
weak_ptr 不会增加引用计数,因此可打破循环。

⚙️ 二、代码理解题

6. 下面的代码会不会内存泄漏?为什么?

struct B;
struct A {
    std::shared_ptr<B> bptr;
    ~A() { std::cout << "A destroyed\n"; }
};

struct B {
    std::shared_ptr<A> aptr;
    ~B() { std::cout << "B destroyed\n"; }
};

int main() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    a->bptr = b;
    b->aptr = a;
}
答:
会内存泄漏。
ab 互相持有 shared_ptr,形成循环引用,引用计数都不为 0,析构函数不会被调用。

解决方法:

struct B {
    std::weak_ptr<A> aptr; // 改为弱引用
};

7. shared_ptrweak_ptr 如何协同工作?

std::shared_ptr<int> sp = std::make_shared<int>(10);
std::weak_ptr<int> wp = sp; // 不增加引用计数

if (auto p = wp.lock()) {   // 尝试获取 shared_ptr
    std::cout << *p << std::endl;
} else {
    std::cout << "对象已被销毁\n";
}
8. 如何自定义 shared_ptr 的删除器(deleter)?

#include <memory>
#include <iostream>

struct FileCloser {
    void operator()(FILE* fp) const {
        if (fp) fclose(fp);
        std::cout << "File closed\n";
    }
};

int main() {
    std::shared_ptr<FILE> file(fopen("data.txt", "r"), FileCloser());
}
自定义删除器可用于释放非 new 分配的资源(如文件、socket 等)。

🧮 三、进阶题

9. shared_ptr 是线程安全的吗?答:

  • 对同一个 shared_ptr 实例的引用计数操作是线程安全的;

  • 但对同一资源的读写操作不是线程安全的

  • 若多个线程同时修改底层对象,需要加锁或使用 std::atomic

10. 智能指针与原始指针混用会出什么问题?

  • 不能用 shared_ptr 管理一个已经由 shared_ptr 管理的原始指针;

  • 否则会导致重复释放(double free)

int* p = new int(10);
std::shared_ptr<int> sp1(p);
std::shared_ptr<int> sp2(p); // ❌ 两个不同控制块,析构两次!
✅ 正确写法:

auto sp1 = std::make_shared<int>(10);
auto sp2 = sp1; // 共享同一控制块
11. 为什么推荐使用 std::make_shared

  • 避免两次内存分配;

  • 提高性能;

  • 防止异常安全问题。

std::make_shared 会在一次内存分配中同时创建控制块和对象,减少开销。

12. 智能指针可以管理数组吗? 答:

  • shared_ptr 可以管理数组,但需要提供自定义删除器;

  • unique_ptr 支持数组版本:std::unique_ptr<int[]> arr(new int[10]);

13. shared_ptr 的循环引用一定要用 weak_ptr 吗?答:

几乎是的。
如果你能确定某个方向的引用不应控制对象生命周期(比如父引用子为强引用,子引用父为弱引用),就应使用 weak_ptr

14. 能否从 unique_ptr 转换成 shared_ptr

通过 std::move 转移所有权:

std::unique_ptr<int> up = std::make_unique<int>(42);
std::shared_ptr<int> sp = std::move(up); // ok

但反过来(shared_ptrunique_ptr)不行。

✅ 总结建议

使用场景推荐指针类型
单一所有者std::unique_ptr
多个共享所有者std::shared_ptr
打破循环依赖std::weak_ptr
兼容旧代码(不推荐)std::auto_ptr(已废弃)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值