日常分享整合的学习笔记:

适合初学者阅读

版本:C++11 及以上版本

内容包含:auto_ptr(了解即可)、unique_ptrshared_ptrweak_ptr、原理解析、使用场景、常见坑点。


一、为什么需要智能指针?

在 C++ 中,动态内存通常需要我们手动管理。

例如:

int* p = new int(10);

使用完成后:

delete p;

如果忘记 delete

void test()
{
    int* p = new int(10);
}

那么这块内存就永远无法释放。

这就叫:

内存泄漏(Memory Leak)

程序运行时间越久:

  • 内存占用越来越高

  • 系统越来越卡

  • 最终可能崩溃

尤其在:

  • 服务器

  • 网络编程

  • 游戏引擎

  • 数据库

中非常危险。


二、什么是智能指针?

智能指针本质上:

是一个类

它内部封装了普通指针。

核心思想:

RAII(资源获取即初始化)

即:

  • 对象创建时获取资源

  • 对象销毁时自动释放资源

这样就不用手动 delete 了。

例如:

{
    std::unique_ptr<int> p(new int(10));
}

代码块结束后:

p

会自动析构。

析构函数内部自动:

delete

因此不会内存泄漏。


三、C++ 中的智能指针种类

C++11 后主要有三种:

智能指针 作用
unique_ptr 独占所有权
shared_ptr 共享所有权
weak_ptr 解决 shared_ptr 循环引用

此外还有:

auto_ptr

但已经被废弃。

了解即可。


四、auto_ptr(已废弃)

早期 C++ 提供的智能指针。

例如:

std::auto_ptr<int> p1(new int(10));

问题在于:

std::auto_ptr<int> p2 = p1;

此时:

  • p2 获得资源

  • p1 变为空

也就是说:

所有权会偷偷转移

非常危险。

因此 C++11 后废弃。

不要再使用。


五、unique_ptr(重点)

1、什么是 unique_ptr?

std::unique_ptr

表示:

独占资源

同一时间:

  • 只能有一个 unique_ptr 管理资源

类似:

“这个对象只属于我”


2、基本使用

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

//示例1:
int main()
{
    unique_ptr<int> p(new int(100));     // 分配内存,构造 unique_ptr,其作用域范围为最近的{}

    cout << *p << endl; //输出 100

    return 0;
}// ← 离开作用域,p 析构,自动 delete 内存

------------------------------------------------------------------------------

//示例2:
int main()
{
    {
        unique_ptr<int> p(new int(100));  // 分配内存
        cout << *p << endl;                // 使用
    }  // ← 离开作用域,p 析构,自动 delete 内存

    return 0;
}

输出:

100

程序结束:

自动释放内存。


3、为什么不能拷贝?

错误代码:

unique_ptr<int> p1(new int(10));
unique_ptr<int> p2 = p1;

会直接报错。

因为:

unique_ptr 不允许多个对象同时管理同一块内存

否则会出现:

delete 两次

程序崩溃。


4、移动语义( move( ) )

unique_ptr 支持:

move 转移所有权

例如:

unique_ptr<int> p1(new int(10));
unique_ptr<int> p2 = move(p1);

此时:

p1 == nullptr

资源已经转移给:

p2

5、推荐使用 make_unique

不推荐:

unique_ptr<int> p(new int(10));

推荐:

auto p = make_unique<int>(10); //使用auto自动推导

优点:

  • 更安全

  • 更简洁

  • 避免异常问题


6、管理数组

unique_ptr<int[]> p(new int[5]);

使用:

p[0] = 100;

注意:

数组版本内部会自动调用:

delete[]

六、shared_ptr(重点)

1、什么是 shared_ptr?

shared_ptr

表示:

多个智能指针共享同一资源

例如:

auto p1 = make_shared<int>(10);//make_shared<int>(10) 在堆上分配一块内存,存储整数 10
auto p2 = p1; //拷贝构造,不会分配新内存,只是增加引用计数,auto 自动推导为 shared_ptr<int>

此时:

  • p1 管理资源

  • p2 也管理资源

资源不会立刻释放。

只有:

所有 shared_ptr 都销毁

资源才会释放。


2、引用计数

shared_ptr 内部维护:

引用计数

例如:

auto p1 = make_shared<int>(10);

计数:

1

再拷贝:

auto p2 = p1;

计数变成:

2

再拷贝:

auto p3 = p1;

计数:

3

可以查看:

cout << p1.use_count() << endl; //输出3

//注:
//use_count() 是 C++ 中 shared_ptr 的一个成员函数,
//用于返回当前与 shared_ptr 共享同一块内存的所有智能指针的数量(即引用计数)

3、示例代码

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

int main()
{
    shared_ptr<int> p1 = make_shared<int>(100);  //像上面一样使用自动推导auto p1也行

    cout << p1.use_count() << endl; //输出1

    shared_ptr<int> p2 = p1;    //也可以使用auto

    cout << p1.use_count() << endl; //输出2
    cout << p2.use_count() << endl; //输出2

    return 0;
}

输出:

1
2
2

4、shared_ptr 的内部原理

shared_ptr 内部大概包含:

class SharedPtr
{
private:
    int* ptr;
    int* count;
};

其中:

count

记录引用数量。

每次拷贝:

(*count)++;

析构时:

(*count)--;

当计数为 0:

delete ptr;

5、不要这样写

错误示例:

int* p = new int(10);

//直接使用裸指针构造
shared_ptr<int> p1(p);
shared_ptr<int> p2(p);

问题:

两个 shared_ptr 会认为自己是第一次管理资源

最终:

delete 两次

正确做法:

auto p1 = make_shared<int>(10);
auto p2 = p1;

七、weak_ptr(重点难点)

1、为什么需要 weak_ptr?

因为:

shared_ptr 存在循环引用问题


2、什么是循环引用?

例如:

class B;

class A
{
public:
    shared_ptr<B> pb;
};

class B
{
public:
    shared_ptr<A> pa;
};

创建对象:

shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());

pa->pb = pb;  // A对象的pb成员指向B对象
pb->pa = pa;  // B对象的pa成员指向A对象

此时:

  • A 引用 B

  • B 引用 A

结果:

引用计数永远不为 0

因此:

内存泄漏


3、weak_ptr 的作用

weak_ptr

表示:

弱引用

特点:

  • 不增加引用计数

  • 不拥有资源

  • 只是观察资源


4、解决循环引用

class B;

class A
{
public:
    weak_ptr<B> pb;
};

class B
{
public:
    weak_ptr<A> pa;
};

这样:

weak_ptr 不会增加计数

对象最终可以正常释放。


5、lock() 使用

weak_ptr 不能直接访问对象。

需要:

lock()

例如:

weak_ptr<int> wp;

if(auto sp = wp.lock())
{
    cout << *sp << endl;
}

lock 后:

  • 如果对象存在

    • 返回 shared_ptr

  • 如果对象已经释放

    • 返回 nullptr


八、智能指针常见函数

1、reset()

释放当前资源。

p.reset();

也可以重新绑定:

p.reset(new int(20));

2、get()

获取原始指针。

int* raw = p.get();

注意:

⚠️不要手动 delete

⚠️否则会崩溃。


3、release()

仅 unique_ptr 有。

作用:

放弃所有权

例如:

unique_ptr<int> p(new int(10));

int* raw = p.release();

此时:

p == nullptr

需要手动:

delete raw;

九、智能指针的底层思想

核心其实是:

析构函数自动释放资源

例如:

class SmartPtr
{
private:
    int* ptr;

public:
    SmartPtr(int* p)
    {
        ptr = p;
    }

    ~SmartPtr()
    {
        delete ptr;
    }
};

当对象离开作用域:

~SmartPtr()

自动调用。

这就是 RAII 思想。


十、智能指针使用场景

1、unique_ptr 适合

  • 独占资源

  • 类成员

  • STL 容器

  • 文件对象

  • socket对象

例如:

vector<unique_ptr<int>> vec;

2、shared_ptr 适合

  • 多处共享资源

  • 线程池

  • 网络连接对象

  • 回调系统


3、weak_ptr 适合

  • 观察对象

  • 解决循环引用

  • 缓存系统


十一、实际开发建议

推荐优先级

unique_ptr > shared_ptr > 原始指针

原因:

  • unique_ptr 开销最小

  • shared_ptr 有引用计数

  • shared_ptr 存在线程安全成本


十二、shared_ptr 的线程安全

很多人误解:

shared_ptr 不是完全线程安全

准确来说:

  • 引用计数是线程安全的

  • 对对象本身访问不是线程安全的

例如:

shared_ptr<Test> p;

多个线程同时:

p->data++;

依然需要加锁。


十三、智能指针常见问题

1、智能指针解决了什么问题?

答:

  • 自动释放内存

  • 防止内存泄漏

  • RAII思想


2、unique_ptr 和 shared_ptr 区别?

unique_ptr shared_ptr
独占 共享
不可拷贝 可拷贝
开销小 有引用计数开销
推荐优先使用 多人共享时使用

3、为什么需要 weak_ptr?

答:

解决 shared_ptr 循环引用


4、shared_ptr 底层原理?

答:

  • 引用计数

  • 计数加减

  • 为 0 自动释放


十四、完整示例

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

class Person
{
public:
    Person()
    {
        cout << "构造函数" << endl;
    }

    ~Person()
    {
        cout << "析构函数" << endl;
    }
};

int main()
{
    {
        shared_ptr<Person> p1 = make_shared<Person>();

        cout << p1.use_count() << endl;

        {
            shared_ptr<Person> p2 = p1;

            cout << p1.use_count() << endl;
        }

        cout << p1.use_count() << endl;
    }

    return 0;
}

输出:

构造函数
1
2
1
析构函数

十五、总结

智能指针核心思想

RAII

即:

用对象生命周期管理资源生命周期


三大智能指针总结

智能指针 特点
unique_ptr 独占资源
shared_ptr 共享资源
weak_ptr 弱引用,不增加计数

推荐

能用 unique_ptr 就不用 shared_ptr

因为:

  • 更轻量

  • 更安全

  • 性能更好


最后一句

对于现代 C++:

尽量不要再直接使用 new/delete

而应该:

make_unique
make_shared

这已经是现代 C++ 开发的重要习惯。

-------------------------------------------------------------------------------------------------
如果文章对你有帮助的话,请点一个小小的赞谢谢

❤️么么么么么么么哒❤️

Logo

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

更多推荐