目录

一、前置认知:C++ 内存痛点(为什么需要RAII与智能指针)

1.1 原生裸指针的致命问题

1.2 传统手动释放的弊端示例

二、RAII 机制精讲(C++内存管理核心基石)

2.1 RAII 是什么?(面试必背定义)

2.2 RAII 核心原理(满分理解)

2.3 手写简易RAII内存管理器(吃透底层)

2.4 RAII 通用场景(不止内存)

三、智能指针核心认知

3.1 什么是智能指针?

3.2 C++四类标准智能指针(全覆盖)

四、unique_ptr 独占智能指针

4.1 核心特性

4.2 基础用法实战

4.3 手动释放/重置资源

4.4 unique_ptr 工程适用场景

五、shared_ptr 共享智能指针

5.1 核心原理:引用计数

5.2 基础用法实战

5.3 引用计数增减规则

计数增加场景

计数减少场景

5.4 shared_ptr 致命缺陷:循环引用

5.5 面试手撕:手写简易 shared_ptr

5.6 智能指针定制删除器

5.6.1 shared_ptr 定制删除器(无类型差异、使用简单)

写法对比1:普通默认删除 VS 定制删除器(数组场景)

写法对比2:文件资源默认空释放 VS 定制关闭释放

5.6.2 unique_ptr 定制删除器(重点易错)

写法对比3:unique_ptr 默认写法 VS 定制删除器写法

最优写法:仿函数删除器

最优写法:仿函数删除器(unique_ptr 类型稳定零开销)

重点补充:unique_ptr 如何传入 Lambda 删除器(面试常问)

5.6.3 make系列函数不支持自定义删除器

5.6.4 默认删除器 VS 定制删除器 核心对比表

5.6.5 高频易错点总结

六、weak_ptr 弱引用智能指针(解循环引用神器)

6.1 核心特性

6.2 核心用法 + 解决循环引用

6.3 weak_ptr 核心成员函数

七、废弃指针 auto_ptr(面试了解即可)

八、四类智能指针终极对比(面试必背表格)

九、高频易错点 + 工程禁忌(避坑必背)

十、面试满分问答

10.1 什么是RAII机制?

10.2 unique_ptr和shared_ptr的区别?

10.3 shared_ptr为什么会内存泄漏?如何解决?

10.4 weak_ptr的作用是什么?

10.5 为什么优先使用make_shared/make_unique?

十一、全文总结


定位:独立完整、零基础入门、刷题工程刚需、期末必考、秋招面试核心重难点全覆盖

文说明:全文无任何前置依赖,从内存泄漏痛点、RAII核心思想入手,逐层拆解四类智能指针,包含底层原理、代码实战、场景对比、循环引用问题与解决方案,所有代码可直接编译运行,完全对标高校考点与互联网面试手撕标准。

一、前置认知:C++ 内存痛点(为什么需要RAII与智能指针)

1.1 原生裸指针的致命问题

C++ 原生手动内存管理(new/delete)存在大量无法规避的隐患,也是程序内存泄漏、崩溃的核心根源:

  • 忘记释放内存:业务逻辑复杂时,极易遗漏 delete,造成内存泄漏

  • 提前返回漏释放:函数多分支return,部分分支跳过delete

  • 异常触发漏释放:代码抛异常,直接终止执行,后续delete永远不执行

  • 重复释放/野指针:多次delete同一指针、释放后继续访问内存,程序直接崩溃

1.2 传统手动释放的弊端示例

#include <iostream>
using namespace std;

void test()
{
    // 手动申请堆内存
    int* p = new int(100);

    // 中途返回,内存泄漏
    if (*p > 0)
        return;  

    // 永远无法执行
    delete p;
}

int main()
{
    test();
    return 0;
}

核心痛点手动管理内存,完全依赖程序员自律,极不可靠,由此诞生C++核心内存管理机制——RAII。

二、RAII 机制精讲(C++内存管理核心基石)

2.1 RAII 是什么?(面试必背定义)

RAII:全称 Resource Acquisition Is Initialization,中文译为 资源获取即初始化

它是C++独有的资源自动管理编程思想,核心逻辑:利用栈对象的生命周期,托管堆资源/系统资源

2.2 RAII 核心原理(满分理解)

C++栈对象有两个铁律:

  1. 栈对象创建时,自动调用构造函数

  2. 栈对象出作用域时,必然自动调用析构函数,无论正常结束、中途return、异常抛出

RAII 思想落地:

  • 构造函数:完成资源申请(new、打开文件、申请锁、网络连接)

  • 析构函数:完成资源释放(delete、关闭文件、解锁、断开连接)

终极价值:资源托管给栈对象,无需手动释放,绝对不会泄漏

2.3 手写简易RAII内存管理器(吃透底层)

#include <iostream>
using namespace std;

// 自研简易RAII内存托管类
template<typename T>
class RAII_Ptr
{
private:
    T* ptr; // 托管裸指针
public:
    // 构造:申请资源
    RAII_Ptr(T* p = nullptr) : ptr(p) {}

    // 析构:自动释放资源
    ~RAII_Ptr()
    {
        if (ptr != nullptr)
        {
            delete ptr;
            ptr = nullptr;
            cout << "资源自动释放" << endl;
        }
    }

    // 重载解引用、箭头运算符,模拟指针用法
    T& operator*() { return *ptr; }
    T* operator->() { return ptr; }
};

void func()
{
    // 栈对象托管堆内存
    RAII_Ptr<int> p(new int(999));
    cout << *p << endl;

    // 无论中途return还是抛异常,析构必然执行,资源自动释放
    return;
}

int main()
{
    func();
    return 0;
}

2.4 RAII 通用场景(不止内存)

RAII 是通用资源管理思想,不仅仅用于内存管理:

  • 智能指针:管理堆内存(核心应用)

  • lock_guard:管理互斥锁,自动加锁解锁

  • fstream文件流:自动打开、关闭文件

  • 网络套接字、数据库连接、句柄资源管理

三、智能指针核心认知

3.1 什么是智能指针?

智能指针 = 基于RAII机制封装的指针管理类

本质是一个栈对象,内部封装原生裸指针,依靠RAII机制实现内存自动释放,杜绝内存泄漏。

3.2 C++四类标准智能指针(全覆盖)

全部定义在 <memory> 头文件中,C++11及以后标准:

  • std::unique_ptr:独占式智能指针(独占所有权,无拷贝,工程最常用)

  • std::shared_ptr:共享式智能指针(引用计数,多指针共享资源)

  • std::weak_ptr:弱引用指针(不计数,解决shared_ptr循环引用)

  • std::auto_ptr:C++98废弃,C++11彻底弃用(缺陷严重,仅面试了解)

四、unique_ptr 独占智能指针

4.1 核心特性

  • 独占所有权:同一时刻,只能有一个unique_ptr托管资源

  • 禁止拷贝构造、禁止赋值:杜绝多指针共享同一资源

  • 支持移动语义:可以通过std::move转移资源所有权

  • 出作用域自动释放,性能极高、无额外开销

4.2 基础用法实战

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    // 方式1:直接初始化
    unique_ptr<int> p1(new int(100));
    cout << *p1 << endl;

    // 方式2:make_unique(C++14推荐,更安全)
    auto p2 = make_unique<int>(200);
    cout << *p2 << endl;

    // 禁止拷贝!编译报错
    // unique_ptr<int> p3 = p2;  

    // 允许移动:所有权转移,p2变为空指针
    unique_ptr<int> p3 = move(p2);
    cout << *p3 << endl;

    return 0;
}

4.3 手动释放/重置资源

int main()
{
    auto p = make_unique<int>(666);
    
    // 主动释放资源
    p.reset(); 

    // 重置托管新资源
    p.reset(new int(888));
    cout << *p << endl;

    return 0;
}

4.4 unique_ptr 工程适用场景

  • 绝大多数单一归属的堆资源管理

  • 类成员指针、局部临时资源、容器存储独占资源

  • 默认首选:工程中优先用unique_ptr,性能优于shared_ptr

五、shared_ptr 共享智能指针

5.1 核心原理:引用计数

shared_ptr 采用引用计数机制,允许多个指针共享同一份堆资源:

  • 资源自带一个计数器,初始为1

  • 新增一个指针共享,计数+1

  • 指针销毁/重置,计数-1

  • 计数归0时,自动释放堆资源

5.2 基础用法实战

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    // make_shared 推荐用法(效率更高,一次性分配内存)
    shared_ptr<int> p1 = make_shared<int>(100);
    cout << "当前计数:" << p1.use_count() << endl; // 1

    // 允许拷贝,共享资源,计数+1
    shared_ptr<int> p2 = p1;
    cout << "当前计数:" << p1.use_count() << endl; // 2

    // p2重置,计数-1
    p2.reset();
    cout << "当前计数:" << p1.use_count() << endl; // 1

    // 出作用域 p1销毁,计数归0,资源释放
    return 0;
}

5.3 引用计数增减规则

计数增加场景

  • 拷贝构造 shared_ptr

  • shared_ptr 赋值给另一个同类型指针

  • 作为函数参数值传递

计数减少场景

  • shared_ptr 出作用域析构

  • 调用reset()主动重置

  • 被其他指针覆盖赋值

5.4 shared_ptr 致命缺陷:循环引用

多个shared_ptr互相持有对方,导致引用计数永远无法归0,资源永久泄漏

#include <iostream>
#include <memory>
using namespace std;

// 循环引用案例(内存泄漏)
class B;
class A
{
public:
    shared_ptr<B> b_ptr;
    ~A() { cout << "A析构" << endl; }
};

class B
{
public:
    shared_ptr<A> a_ptr;
    ~B() { cout << "B析构" << endl; }
};

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

    // 互相持有,循环引用
    a->b_ptr = b;
    b->a_ptr = a;

    // 出作用域,计数无法清零,无析构打印,内存泄漏
    return 0;
}

问题根源:a、b互相强引用,各自计数始终为1,永远不会触发释放。

5.5 面试手撕:手写简易 shared_ptr

面试高频手撕题:面试官常要求手写简易版shared_ptr,只要实现引用计数、拷贝构造、赋值重载、析构释放核心逻辑即可,吃透原生shared_ptr底层原理。

核心设计思路

  • 模板类,适配任意类型数据

  • 两个成员变量:T* 资源裸指针、int* 引用计数指针(堆上计数,所有共享指针共用)

  • 拷贝构造/赋值:资源计数+1

  • 析构:计数-1,计数归0则释放资源与计数空间

#include <iostream>
using namespace std;

// 手写简易shared_ptr(面试标准版)
template<typename T>
class MySharedPtr
{
private:
    T* _ptr;        // 托管的堆资源指针
    int* _refCount; // 引用计数(堆分配,多指针共享)

public:
    // 1. 构造函数:初始化资源,计数置1
    MySharedPtr(T* p = nullptr) : _ptr(p)
    {
        if (p)
            _refCount = new int(1);
        else
            _refCount = new int(0);
    }

    // 2. 拷贝构造:共享资源,计数+1
    MySharedPtr(const MySharedPtr<T>& other)
    {
        _ptr = other._ptr;
        _refCount = other._refCount;
        if (_ptr)
            (*_refCount)++;
    }

    // 3. 赋值运算符重载:处理新旧资源计数
    MySharedPtr<T>& operator=(const MySharedPtr<T>& other)
    {
        // 防止自赋值
        if (this == &other)
            return *this;

        // 旧资源计数-1,归0则释放
        if (_ptr && --(*_refCount) == 0)
        {
            delete _ptr;
            delete _refCount;
        }

        // 接管新资源
        _ptr = other._ptr;
        _refCount = other._refCount;
        if (_ptr)
            (*_refCount)++;

        return *this;
    }

    // 4. 析构函数:计数-1,归0释放资源
    ~MySharedPtr()
    {
        if (_ptr && --(*_refCount) == 0)
        {
            delete _ptr;
            delete _refCount;
            cout << "资源彻底释放" << endl;
        }
    }

    // 重载指针运算符
    T& operator*() { return *_ptr; }
    T* operator->() { return _ptr; }

    // 获取当前引用计数
    int use_count() const { return *_refCount; }
};

// 测试代码
int main()
{
    MySharedPtr<int> p1(new int(100));
    cout << "p1计数:" << p1.use_count() << endl; // 1

    MySharedPtr<int> p2 = p1;
    cout << "拷贝后计数:" << p1.use_count() << endl; // 2

    MySharedPtr<int> p3(new int(200));
    p3 = p1; // 赋值,p3旧资源释放,p1资源计数+1
    cout << "赋值后计数:" << p1.use_count() << endl; // 3

    return 0;
}

手撕必背考点解析

  1. 计数存在堆上:引用计数不能是成员变量,必须new在堆上,保证多个共享指针共用同一个计数

  2. 赋值必须防自赋值:避免自己赋值自己导致资源误释放

  3. 先减后判释放:赋值和析构时,先对旧资源计数-1,为0才释放资源和计数内存

  4. 完全契合原生逻辑:核心逻辑和标准库shared_ptr一致,满足面试手撕满分要求

5.6 智能指针定制删除器

核心场景:默认智能指针只能用 delete 释放堆内存,但若需要释放数组、文件句柄、套接字、自定义资源,必须使用定制删除器

删除器本质:一个可调用对象(普通函数、Lambda、仿函数),在智能指针析构时替代默认的delete,执行自定义资源释放逻辑。

核心学习重点:对比「智能指针默认删除写法」和「定制删除器写法」,清晰掌握二者差异、适用场景与底层区别,彻底吃透该面试高频考点。

核心场景:默认智能指针只能用 delete 释放堆内存,但若需要释放数组、文件句柄、套接字、自定义资源,必须使用定制删除器

删除器本质:一个可调用对象(普通函数、Lambda、仿函数),在智能指针析构时替代默认的delete,执行自定义资源释放逻辑。

5.6.1 shared_ptr 定制删除器(无类型差异、使用简单)

shared_ptr的删除器不参与类类型推导,删除器类型不影响智能指针类型,通用性极强,工程最常用。

写法对比1:普通默认删除 VS 定制删除器(数组场景)

① 错误写法:默认删除器释放数组(内存泄漏)

智能指针默认删除器仅调用 delete,只能释放单个堆对象,无法释放数组,会造成数组内存泄漏。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    // 错误!默认delete释放数组,内存泄漏
    shared_ptr<int> wrong_arr(new int[100]);
    return 0;
}

② 正确写法:定制删除器释放数组

自定义Lambda删除器,手动指定 delete[] 释放数组资源,完美解决泄漏问题。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    // Lambda作为定制删除器,使用delete[]释放数组
    shared_ptr<int> arr(new int[100], [](int* p){
        cout << "数组资源自定义释放成功" << endl;
        delete[] p;
    });

    return 0;
}

原生默认delete只会释放单个对象,数组必须用 delete[],否则内存泄漏。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    // Lambda作为定制删除器,使用delete[]释放数组
    shared_ptr<int> arr(new int[100], [](int* p){
        cout << "数组资源释放" << endl;
        delete[] p;
    });

    return 0;
}

写法对比2:文件资源默认空释放 VS 定制关闭释放

① 错误写法:默认删除器托管文件资源(严重BUG)

文件句柄不属于堆内存,默认delete无法关闭文件,会导致文件句柄泄露、文件无法正常保存。

#include <iostream>
#include <memory>
#include <cstdio>
using namespace std;

int main()
{
    // 错误!默认delete无法关闭文件,句柄泄漏
    shared_ptr<FILE> wrong_fp(fopen("test.txt", "w"));
    return 0;
}

② 正确写法:定制删除器关闭文件资源

#include <iostream>
#include <memory>
#include <cstdio>
using namespace std;

int main()
{
    // 托管文件指针,自定义删除器自动关闭文件
    shared_ptr<FILE> fp(fopen("test.txt", "w"), [](FILE* f){
        if(f)
        {
            fclose(f);
            cout << "文件资源自定义关闭成功" << endl;
        }
    });

    return 0;
}

5.6.2 unique_ptr 定制删除器(重点易错)

unique_ptr 删除器类型会参与模板实例化,导致带删除器和默认删除器的unique_ptr类型不同,无法互相赋值、拷贝,这是高频面试坑点。

必须显式指定删除器类型,常用函数指针、仿函数,不推荐直接裸写Lambda(类型不明确)。

写法对比3:unique_ptr 默认写法 VS 定制删除器写法

① unique_ptr 默认写法(仅支持单个堆对象)

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    // 默认删除器,仅适配单个int堆对象
    unique_ptr<int> p(new int(100));
    return 0;
}

② unique_ptr 定制删除器写法(适配数组/自定义资源)

unique_ptr删除器属于模板参数,需要显式声明类型,这里用函数指针实现数组资源安全释放。


#include <iostream>
#include <memory>
using namespace std;

// 自定义删除器函数
void delArr(int* p)
{
    cout << "unique_ptr数组自定义释放成功" << endl;
    delete[] p;
}

int main()
{
    // 显式指定删除器类型,适配数组资源
    unique_ptr<int, void(*)(int*)> arr(new int[10], delArr);
    return 0;
}

最优写法:仿函数删除器

仿函数删除器无运行时开销、类型固定稳定,是unique_ptr定制删除器的工程最优写法,对比临时Lambda、函数指针兼容性更强。

#include <iostream>
#include <memory>
using namespace std;

// 自定义删除器函数
void delArr(int* p)
{
    cout << "unique_ptr数组自定义释放成功" << endl;
    delete[] p;
}

int main()
{
    // 显式指定删除器类型,适配数组资源
    unique_ptr<int, void(*)(int*)> arr(new int[10], delArr);
    return 0;
}

最优写法:仿函数删除器(unique_ptr 类型稳定零开销)

仿函数删除器无运行时开销、类型固定稳定,是unique_ptr定制删除器的工程最优写法,对比临时Lambda、函数指针兼容性更强。

#include <iostream>
#include <memory>
#include <cstdio>
using namespace std;

// 仿函数删除器(无开销、类型固定)
struct FileDel
{
    void operator()(FILE* f) const
    {
        if(f) fclose(f);
        cout << "文件关闭成功" << endl;
    }
};

int main()
{
    unique_ptr<FILE, FileDel> fp(fopen("test.txt", "w"));
    return 0;
}

重点补充:unique_ptr 如何传入 Lambda 删除器(面试常问)

前面提到:unique_ptr 的删除器是模板参数,必须是确定类型。而普通匿名 Lambda 没有具体类型,无法直接作为模板参数写入,会直接编译报错。

正确解决方案:借助 decltype 推导Lambda类型 + constexpr 修饰Lambda,实现unique_ptr接收Lambda删除器,是现代C++工程常用写法。

写法对比:默认写法 VS Lambda定制删除器写法

① unique_ptr 默认删除写法(只能释放单个对象)

// 默认删除器,仅支持普通单个堆内存
unique_ptr<int> p(new int(100));

② unique_ptr + Lambda 定制删除器 标准正确写法(支持数组/自定义资源)

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    // constexpr Lambda:编译期确定类型,适配unique_ptr模板参数
    constexpr auto arr_del = [](int* p) {
        cout << "unique_ptr Lambda删除器释放数组" << endl;
        delete[] p;
    };

    // decltype 推导Lambda类型,完美传入定制删除器
    unique_ptr<int, decltype(arr_del)> arr(new int[20], arr_del);

    return 0;
}

进阶:文件资源 Lambda 删除器实战

#include <iostream>
#include <memory>
#include <cstdio>
using namespace std;

int main()
{
    constexpr auto file_del = [](FILE* f) {
        if(f)
        {
            fclose(f);
            cout << "Lambda删除器关闭文件成功" << endl;
        }
    };

    unique_ptr<FILE, decltype(file_del)> fp(fopen("test.txt", "w"), file_del);
    return 0;
}

面试必背考点

  • 为什么unique_ptr不能直接裸写Lambda:匿名Lambda每次都是不同类型,无法匹配模板参数,编译失败

  • 必须搭配decltype:精准推导Lambda的唯一类型,满足unique_ptr模板类型要求

  • 推荐constexpr修饰:让Lambda编译期确定,无运行时开销,和仿函数性能一致

  • shared_ptr可以直接裸传Lambda,无需指定类型,二者底层机制完全不同

5.6.3 make系列函数不支持自定义删除器

必考知识点make_shared / make_unique无法传入定制删除器

原因:make系列函数统一封装内存分配逻辑,固化了默认删除方式,因此需要自定义删除器时,必须用new原生初始化

5.6.4 默认删除器 VS 定制删除器 核心对比表

对比维度

默认删除器

定制删除器

释放方式

固定调用 delete

自定义:delete[]/fclose/自定义释放逻辑

支持资源类型

仅单个堆对象

数组、文件、套接字、第三方资源等所有资源

shared_ptr类型影响

不改变指针类型,兼容性强

unique_ptr类型影响

改变模板类型,需显式指定

适用场景

普通单个堆内存资源

特殊资源、数组、非内存资源管理

  • shared_ptr:删除器不改变指针类型,灵活通用,可自由赋值、容器存储,推荐Lambda删除器

  • unique_ptr:删除器属于模板参数,改变指针类型,类型严格,推荐仿函数删除器(零开销)

5.6.5 高频易错点总结

  • 智能指针托管数组资源必须定制删除器,否则默认delete释放数组造成内存泄漏

  • unique_ptr删除器会改变类型,不能与默认unique_ptr混用

  • 自定义删除器场景:数组、文件、套接字、句柄、第三方库资源

  • 需要删除器时禁止使用make_shared/make_unique

六、weak_ptr 弱引用智能指针(解循环引用神器)

6.1 核心特性

  • 弱引用、不参与计数:持有资源不增加引用计数

  • 依赖shared_ptr:只能由shared_ptr构造

  • 不管理资源生命周期:仅做访问观测,不负责释放

  • 可判断资源是否失效:通过expired()检测资源是否已释放

6.2 核心用法 + 解决循环引用

#include <iostream>
#include <memory>
using namespace std;

class B;
class A
{
public:
    shared_ptr<B> b_ptr;
    ~A() { cout << "A析构" << endl; }
};

class B
{
public:
    // 改为弱指针,不计数,打破循环引用
    weak_ptr<A> a_ptr;
    ~B() { cout << "B析构" << endl; }
};

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

    a->b_ptr = b;
    b->a_ptr = a; // weak_ptr赋值,不增加计数

    // 正常析构,无内存泄漏
    return 0;
}

6.3 weak_ptr 核心成员函数

  • expired():判断资源是否已释放(true=失效)

  • lock():将weak_ptr提升为shared_ptr,安全访问资源

int main()
{
    shared_ptr<int> p1 = make_shared<int>(999);
    weak_ptr<int> wp = p1;

    if (!wp.expired())
    {
        // 提升为强指针访问
        shared_ptr<int> p2 = wp.lock();
        cout << *p2 << endl;
    }

    p1.reset();
    if (wp.expired())
    {
        cout << "资源已释放" << endl;
    }
    return 0;
}

七、废弃指针 auto_ptr(面试了解即可)

C++98老式智能指针,C++11完全废弃、禁止使用

致命缺陷:拷贝时强制转移所有权,原指针置空,极易引发野指针崩溃,安全性极差,被unique_ptr完全替代。

八、四类智能指针终极对比(面试必背表格)

智能指针

所有权

引用计数

拷贝/移动

核心用途

unique_ptr

独占

禁止拷贝、支持移动

独占资源管理、高性能、默认首选

shared_ptr

共享

支持拷贝、支持移动

多指针共享资源、多线程共享对象

weak_ptr

无所有权

不计数

支持拷贝

解决循环引用、安全观测资源

auto_ptr

独占

拷贝偷权、不安全

C++11废弃,禁止使用

九、高频易错点 + 工程禁忌(避坑必背)

  • 禁止裸指针多托管:同一个裸指针不能同时交给两个unique_ptr/shared_ptr管理,重复释放崩溃

  • unique_ptr禁止拷贝:需要共享资源必须用shared_ptr

  • shared_ptr必防循环引用:双向依赖场景必须一端用weak_ptr

  • weak_ptr不能直接解引用:必须lock()提升为shared_ptr后访问

  • 优先make_xxx创建:make_unique/make_shared比new初始化更安全、内存效率更高

  • 不要用智能指针托管栈内存:仅用于堆内存管理,托管栈内存会导致非法释放崩溃

十、面试满分问答

10.1 什么是RAII机制?

RAII即资源获取即初始化,是C++核心资源管理思想。利用栈对象生命周期特性,在构造函数中申请资源,析构函数中释放资源,栈对象出作用域自动析构,实现资源自动释放,彻底解决手动内存泄漏问题。

10.2 unique_ptr和shared_ptr的区别?

unique_ptr是独占式智能指针,无引用计数,禁止拷贝、仅支持移动,性能高,适用于单一资源归属;shared_ptr是共享式智能指针,基于引用计数实现多指针共享资源,支持拷贝赋值,有少量计数开销,适用于多对象共享资源场景。

10.3 shared_ptr为什么会内存泄漏?如何解决?

多个shared_ptr互相持有对方强引用会形成循环引用,导致引用计数无法归零,资源无法释放造成内存泄漏。解决方案是将其中一方的shared_ptr替换为weak_ptr,弱引用不参与计数,打破循环引用闭环。

10.4 weak_ptr的作用是什么?

weak_ptr是弱引用智能指针,不增加资源引用计数,不拥有资源所有权,主要用于解决shared_ptr循环引用问题,同时可以安全检测资源是否失效,避免野指针访问崩溃。

10.5 为什么优先使用make_shared/make_unique?

相比new直接初始化,make系列函数一次性分配内存,内存布局更紧凑、效率更高,同时可以避免裸指针二次托管、临时内存泄漏等安全问题,代码更简洁规范。

十一、全文总结

1、RAII是C++资源管理核心思想,依托栈对象生命周期,实现资源自动申请与释放,是智能指针的底层原理;

2、unique_ptr独占资源、高性能、禁止拷贝,是工程默认首选智能指针;

3、shared_ptr基于引用计数实现资源共享,支持多指针共用,存在循环引用泄漏风险;

4、weak_ptr无计数、不持有所有权,核心作用是解决shared_ptr循环引用问题;

5、智能指针彻底替代手动new/delete,是现代C++内存管理的标准规范,杜绝绝大多数内存泄漏与野指针崩溃问题。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐