C++学习笔记(31):智能指针(shared_ptr)

简介

C++ 的 shared_ptr 是 C++11 标准引入的智能指针之一,用于管理动态分配的对象的所有权。它允许多个 shared_ptr 实例共享对同一对象的所有权,而不会出现内存泄漏或者悬空指针的情况。 shared_ptr 使用引用计数技术来跟踪有多少个 shared_ptr 实例指向同一个对象,并在最后一个实例销毁时自动释放对象。

shared_ptr 使用

shred_ptr 叫做共享智能指针,其创建和使用语法如下:

arduino 复制代码
#if 0
#include <iostream>
#include <memory>

using namespace std;

class Person
{
public:
    Person()
    {
        cout << "无参构造函数" << endl;
    }
    Person(int, int)
    {
        cout << "有参构造函数" << endl;
    }

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

// 1. 创建 shared_ptr 对象
void test01()
{
    // 1.1 使用 shared_ptr 的构造函数创建对象
    shared_ptr<Person> sp1(new Person(10, 20));

    // 1.2 使用 shared_ptr 管理动态对象数组
    shared_ptr<Person[]> sp2(new Person[5]);
}


// 2. shared_ptr 操作函数
void test02()
{
    shared_ptr<Person> sp1(new Person(10, 20));
    shared_ptr<Person> sp2(new Person(100, 200));

    // 2.1 get 成员函数可以获得 shared_prt 管理的动态对象指针
    Person* person = sp1.get();

    // 2.2 swap 成员函数可以交换两个 shared_ptr 管理的动态对象指针
    sp1.swap(sp2);

    // 2.3 reset 成员函数存在两个重载版本的函数,其作用分别如下:
    sp1.reset();  // 释放其管理动态指针,此时 sp1 对象管理的动态指针指向为 nullptr
    sp1.reset(new Person(1, 2));  // 释放原来的动态对象,并指向新的动态对象
}

int main()
{
    test01();
    test02();

    return 0;
}

#endif

shared_ptr 特性

多个 shared_ptr 对象能够同时持有并管理同一个动态对象。当 shared_ptr 发生对象拷贝、赋值时都会导致多个 shared_ptr 对象持有同一个动态对象,如下代码所示:

c 复制代码
#if 1
#include <iostream>
#include <memory>
#include <vector>
using namespace std;


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

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

void test()
{
    shared_ptr<Person> sp1(new Person);

    // 1. 允许对象拷贝、对象赋值
    shared_ptr<Person> sp2(sp1);

    shared_ptr<Person> sp3;
    sp3 = sp1;  // 对象赋值

    // 2. 允许对象移动拷贝、移动赋值
    shared_ptr<Person> sp4 = move(sp3);

    shared_ptr<Person> sp5;
    sp5 = move(sp1);  // 移动赋值

    cout << "sp1:" << sp1.get() << endl;  // 已被移动
    cout << "sp2:" << sp2.get() << endl;
    cout << "sp3:" << sp3.get() << endl;  // 已被移动
    cout << "sp4:" << sp4.get() << endl;
    cout << "sp5:" << sp5.get() << endl;

    // 允许存储到容器中
    vector<shared_ptr<Person>> vec;
    vec.push_back(sp2);
    vec.push_back(sp4);
    vec.push_back(sp5);
}

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

#endif

shared_ptr 引用计数

shared_ptr 是通过引用计数的方式来实现对象共享的。那么,什么是引用计数呢?

在 shared_ptr 对象的内部维护了两个非常重要的内容:

  1. 动态创建对象
  2. 引用计数对象

请看下面的示例代码:

c 复制代码
#if 1
#include <iostream>
#include <memory>
using namespace std;


struct Person
{
    Person()
    {
        cout << "Person 构造函数" << endl;
    }

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


void test()
{
    // 初始化智能指针,引用计数为 0
    shared_ptr<Person> sp1(new Person);
    cout << "sp1:" << sp1.use_count() << endl;
    
    // 发生拷贝,引用计数 +1
    shared_ptr<Person> sp2(sp1);
    cout << "sp2:" << sp2.use_count() << endl;

    // 发生赋值,引用计数 + 1
    shared_ptr<Person> sp3;
    sp3 = sp2;
    cout << "sp3:" << sp3.use_count() << endl;
    
    // 判断是否独占资源
    cout << "sp1 是否独占资源:" << sp1.unique() << endl;

    // sp2 释放资源所有权,通过该对方访问的引用次数为 0
    sp2.reset();
    cout << "sp2:" << sp2.use_count() << endl;

    // sp1 和 sp2 引用计数为 2
    cout << "sp1:" << sp1.use_count() << endl;
    cout << "sp3:" << sp3.use_count() << endl;
}

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

shared_ptr 自定义删除器

shared_ptr 和 unique_ptr 一样,并不仅仅管理 new 出来的动态对象,但是,智能指针默认使用 delete、delete[] 来释放被管理的对象。

此时,如果被管理的对象并不是 new、new[] 出来的,需要自定义删除器。其删除器可以是以下任何一种形式:

  1. 普通函数
  2. 函数对象
  3. lambda 匿名函数对象

请看下面的示例代码:

c 复制代码
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include <memory>
#include <iostream>
#include <memory>
using namespace std;


void my_deleter(FILE* fp)
{
    cout << "文件自动关闭" << endl;
    fclose(fp);
    fp = nullptr;
}

struct MyDeleter
{
    void operator()(FILE* fp)
    {
        cout << "文件自动关闭" << endl;
        fclose(fp);
        fp = nullptr;
    }
};


void test()
{
    // 1. 使用普通函数作为自定义删除器
    shared_ptr<FILE> sp1(fopen("./demo.txt", "w"), my_deleter);

    // 2. 使用函数对象作为自定义删除器
    shared_ptr<FILE> sp2(fopen("./demo.txt", "w"), MyDeleter());

    // 3. 使用 lambda 匿名函数对象作为自定义删除器
    shared_ptr<FILE> sp3(fopen("./demo.txt", "w"), [](FILE* fp) {
            cout << "文件自动关闭" << endl;
            fclose(fp);
            fp = nullptr;
        });
}


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

#endif
相关推荐
谭欣辰42 分钟前
LCS(最长公共子序列)详解
开发语言·c++·算法
Cando学算法1 小时前
鸽笼原理(抽屉原理)
c++·算法·学习方法
郝学胜-神的一滴2 小时前
跨平台动态库与头文件:从原理到命名的深度解析
linux·c++·程序人生·unix·cmake
代码中介商2 小时前
C++ 仿函数(Functor)深度解析:从基础到应用
开发语言·c++
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之字符串 --【字符串基础】:[NOIP 2018 普及组] 标题统计
c++·字符串·csp·高频考点·信奥赛·专项训练·标题统计
冯诺依曼的锦鲤2 小时前
从零实现高并发内存池:TCMalloc 核心架构拆解
c++·学习·算法·架构
无忧.芙桃3 小时前
C++IO库的超详细讲解
开发语言·c++
爱看书的小沐4 小时前
【小沐学GIS】基于C++渲染三维飞行仿真Flight Simulation(OpenGL )第十三期
c++·qt·webgl·opengl·飞行仿真·flight
你撅嘴真丑4 小时前
最大质因子序列
c++
努力努力再努力wz4 小时前
【MySQL进阶系列】一文打通事务机制:从锁、Undo Log 到 MVCC 与隔离级别
c语言·数据结构·数据库·c++·mysql·算法·github