C++智能指针之shared_ptr(保姆级教学)

news/2024/7/20 12:46:33 标签: c++, 嵌入式, 内存管理, 智能指针, share_ptr

目录

C++智能指针之shared_ptr

本文涉及所有程序

工作原理

使用方法

手动初始化

std::make_shared函数(C++14提出)

构造方法初始化

make_shared初始化

使用实例

shared_ptr常规操作

use_count();

unique();

reset();

get();

指定删除器

移动语义:std::move();


C++智能指针之shared_ptr

共享式指针:多个指针可以同时指向同一个对象(共享所有权,协同工作),当最后一个指针被销毁或者指向其他对象时,这个对象会被释放;

本文涉及所有程序

00_code.cpp

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

class A
{
public:
    A()
    {
        cout << "A" << endl;
    }
    ~A()
    {
        cout << "~A" << endl;
    }
};

void test(shared_ptr<A> pb)
{
    cout << "test func" << endl;
}

shared_ptr<A>test1()
{
    shared_ptr<A>temp(new A());
    return temp;
}

int main(int argc, char const* argv[])
{
    // int *p = new int(5);//裸指针

    // shared_ptr是类模板
    // shared_ptr<int> p = new int(5);//调用类型转换构造函数初始化 explicit不支持
    shared_ptr<int> p(new int(5));

    cout << *p << endl;

    // string *s = new string("hello world");
    shared_ptr<string> s(new string("hello world"));
    cout << *s << endl;

    // int num = 10;
    // shared_ptr<int>p2(&num);//报错:智能指针指向栈空间时,会导致释放两次空间或者对象

    shared_ptr<A> pa(new A()); //程序运行结束后会自动调用A的析构函数

    shared_ptr<A>result = test1();

    test(pa);

    shared_ptr<A> pa2(pa); // pa2,p同时指向同一个对象或者空间
    return 0;
}

01_code.cpp

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

int main(int argc, char const *argv[])
{
    shared_ptr<int> pi = make_shared<int>(5);

    cout << *pi << endl;

    shared_ptr<string> ps = make_shared<string>("hello world");
    cout << *ps << endl;

    //手动初始化和使用make_shared函数模板初始化有什么区别
    /**
    *推荐使用make_shared进行初始化;开销小,效率高(数据和引用计数存在同一段空间)
    *构造函数初始化:数据和引用计数不在同一段空间中,需要间接访问
    */
    return 0;
}

02_code.cpp

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

class A
{
public:
    A()
    {
        cout << "A" << endl;
    }

    A(int num):m_num(num)
    {
        cout <<"A int"<<endl;
    }

    A(const A&& other):m_num(other.m_num)
    {
        cout <<"A move int"<<endl;
    }

    ~A()
    {
        cout << "~A" << endl;
    }

public:
    int m_num;
};

void test(int *ptr)
{

}

void mydelete(A *pa)
{
    cout <<"call my delete" <<endl;
    delete []pa;
}
int main(int argc, char const *argv[])
{
#if 0
    //shared_ptr<A> pa = make_shared<A>(5); //隐式类型转换
    //cout<< pa->m_num<<endl;

    shared_ptr<A> pb = make_shared<A>(); 
    //cout<< pb->m_num<<endl;
    shared_ptr<A>pc(pb);
    cout << pb.use_count()<<endl;

    if(pb.unique())
    {
        cout << "pb is unique ptr" << endl;
    }

    // pb.reset();
    // if(pb == nullptr)
    // {
    //     cout << "pb is nullptr" << endl;  
    // }
    //shared_ptr<A>pd(new A(1));
    //pb.reset(pd);//reset不支持传入共享指针
    pb.reset(new A(6));
    cout <<  pb.use_count() <<endl;

    shared_ptr<int>pi(new int(19));
    test(pi.get());//void test(int *ptr);
    
#endif
    //指定删除器
    //shared_ptr<A>ptr(new A[3],mydelete);

    //shared_ptr<A[]>ptr(new A[3]);
    // shared_ptr<A>ptr(new A[3],[](A *a)
    // {
    //     delete []a;
    // });

    //shared_ptr<A>ptr(new A[3],default_delete<A[]>());

    shared_ptr<A>ptr(new A(6));
    shared_ptr<A>&&rptr = std::move(ptr);

    cout <<  ptr.use_count() <<endl;
    cout <<  rptr.use_count() <<endl;
    return 0;
}

03_code.cpp

#include <iostream>
using namespace std;

template <typename T>
class shared_ptr
{
public:
    shared_ptr(const shared_ptr<T> &other)
    {
        this->ptr = other.ptr;
        strong_ref++;
    }

    shared_ptr(const T *t)
    {
        this->ptr = t;
        strong_ref++;
    }

    T *get()
    {
        return ptr;
    }

    ~shared_ptr()
    {
        strong_ref--;
        if (strong_ref == 0)
        {
            delete ptr;
        }
    }

private:
    T *ptr;
    static int strong_ref;
};

template <typename T>
int shared_ptr<T>::strong_ref = 0;

int main(int argc, char const *argv[])
{

    return 0;
}

工作原理

引用计数增加/减少(原子操作)

定义:shared_ptr 智能指针变量名

使用方法

手动初始化

注:shared_ptr p = new int(10); //错误,

shared_ptr是explicit,不可以进行隐式类型转换,只能用构造函数直接初始化;

对于shared_ptr s(new string("hello world"));可以写成shared_ptr s("hello world"),因为"hello world"被当成了const char数组来使用(即字符指针)

std::make_shared函数(C++14提出)

auto p = std::make_shared("hello world");

error:auto p = std::make_shared(new int[]);

功能:在 堆内存中可以动态分配对象,并返回一个shared_ptr;

推荐方法,因为此方法安全,高效;

构造方法初始化

假如初始化化了两个智能智能sp1和sp2,它们指向的是同一个空间,使用构造函数进行初始化,指向的这个空间里面存放着强引用计数和弱引用计数以及实际存放变量的地址等

make_shared初始化

而使用函数模板进行初始化,指向的这个空间里面存放着强引用计数和弱引用计数以及实际存放变量等,它们都存储在同一段空间上,因此访问起来更高效

使用实例

1、引用计数增加/减少(原子操作)(以初始化方式一为例)

(1)通过VS2022进行调试,通过打断点,当执行完第一条指令后,引用计数变为1

当执行完第二条指令后,引用计数变为2

程序执行完,引用计数释放

(2)函数传参改变引用计数

初始化第一次,引用计数为1

函数传参,引用计数加1

函数执行完,新参释放,引用计数减1

初始化另一个智能指针,引用计数再次加1

(3)函数返回值为智能指针

函数内的智能指针释放,因此接返回值后,引用计数仍为1

2、make_shared函数进行初始化

对于自定义类型,可以使用隐式类型转换

make_shared也可以对普通变量或对象使用

shared_ptr常规操作

use_count();

功能:返回有多少个shared_ptr智能指针指向某对象;(引用计数的个数)

用途:主要用于调试

unique();

判断当前智能指针是否独享(只有它自己指向该对象)某个对象或者空间,独占返回true,否则返回false;

reset();

  • reset()无参使用:若该智能指针是独占某个对象,则释放该对象,并将智能指针置nullptr;若不独占,引用计数减1,并将该指针置nullptr;
  • reset()代参使用:若该智能指针是独占某个对象,则释放该对象,并将该指针指向新对象;若不独占,则将该指针指向的对象引用计数减1,并将该指针指向新对象;

实例:pb.reset();

pb.reset(new A(6));

get();

功能:获得智能指针中保存的指针(裸指针);主要为了适应C的接口

考虑到有些函数参数是裸指针并不是智能指针,所以需要将智能指针转化为裸指针;

指定删除器

原因:有些情况,默认删除器处理不了(shared_ptr管理动态数组),需要我们自己指定删除器;

对于图中示例,在进行释放的时候,只调用了delete ptr,只释放了一次。而我们希望的是调用delete []ptr将空间全部释放

解决方法1:自定义删除器函数

当默认的删除器不起作用时,需要自己指定删除器:void (*) T(T*);

在shared_ptr的构造函数中函数名作为第二个参数直接传入即可

解决方法2:通过指定类型,直接指定智能指针所执行的是一个数组

解决方法3:lambda表达式

解决方法4:可用标准库模板类default_delete做删除器(函数对象)

移动语义:std::move();

因为对象移动做的不是数据的拷贝,使用右值ptr会认为rptr是个临时变量,因此并不会改变原本引用计数


http://www.niftyadmin.cn/n/4998882.html

相关文章

【jsvue】联合gtp仿写一个简单的vue框架,以此深度学习JavaScript

用 gtp 学习 Vue 生命周期的原理 lifecycle.js function Vue(options) {// 将选项保存到实例的 $options 属性中this.$options options;// 若存在 beforeCreate 钩子函数&#xff0c;则调用之if (typeof options.beforeCreate function) {options.beforeCreate.call(this);…

软件产品确认测试鉴定测试

软件产品确认测试 确认测试也称鉴定测试&#xff0c;即验证软件的功能、性能及其它特性是否与用户的要求一致。软件确认测试是在模拟的环境下&#xff0c;验证软件是否满足需求规格说明书列出的需求。为此&#xff0c;需要首先制定测试计划&#xff0c;规定要做测试的种类&…

CXL.mem S2M Message 释义

&#x1f525;点击查看精选 CXL 系列文章&#x1f525; &#x1f525;点击进入【芯片设计验证】社区&#xff0c;查看更多精彩内容&#x1f525; &#x1f4e2; 声明&#xff1a; &#x1f96d; 作者主页&#xff1a;【MangoPapa的CSDN主页】。⚠️ 本文首发于CSDN&#xff0c…

面向深度神经网络的特定领域架构

随着AI对算力的需求不断增长&#xff0c;以TPU为代表的面向DNN的特定领域架构为DNN计算提供了几十倍的性能提升以及能效优化。本文基于谷歌真实业务场景数据&#xff0c;介绍了TPU相对CPU/GPU的实际性能、能效指标。原文: A Domain-Specific Architecture for Deep Neural Netw…

windows 2012服务器配置nginx后无法访问域名的问题

环境&#xff1a;Windows 2012 R2 Nginx 问题&#xff1a;确认域名解析到服务器ip已生效&#xff08;通过ping域名地址确认域名已指向该ip&#xff09;&#xff0c;确认nginx配置无误&#xff08;绑定域名、配置端口、配置网站文件目录&#xff09;&#xff0c;但无法从外网访…

Python小知识 - 1. Python装饰器(decorator)

Python装饰器&#xff08;decorator&#xff09; Python装饰器是一个很有用的功能&#xff0c;它可以让我们在不修改原有代码的情况下&#xff0c;为已有的函数或类添加额外的功能。 常见的使用场景有&#xff1a; a. 函数缓存&#xff1a;对于一些计算量较大的函数&#xff0c…

TIA博途从V15.1版本升级到V16后,下载配方时出错,动作异常终止

TIA博途从V15.1版本升级到V16后,下载配方时出错,动作异常终止 1. 读取配方的时候没有问题,完全正常,没有任何错误提示。 2. 但是在下载的时候,就提示了“出错。动作异常终止” 根据以往的经验分析,有可能是配方变量里面没有相对应的地址时候下载会出错,但是配方画面相对…

int atexit(void (*func)(void))

int atexit(void (*func)(void)) 是一个函数原型&#xff0c;它用于注册一个函数&#xff0c;在程序正常退出时自动调用该函数。 函数原型中的 void (*func)(void) 表示一个函数指针&#xff0c;它指向一个无返回值且无参数的函数。你可以将一个函数的地址传递给 atexit 函数&…