首页 > 其他分享 >共享智能指针

共享智能指针

时间:2023-12-12 15:12:30浏览次数:29  
标签:use cout ptr1 智能 shared 共享 ptr 指针

文章参考:

爱编程的大丙 (subingwen.cn)

所谓智能指针,其实就是C++11封装的类,里面存有一个正常指针,智能指针会通过这个正常指针,来监视指针指向的内存,当没有智能指针指向该内存时,该内存就被释放。其核心在于引用计数,每一个智能指针指向内存A,智能指针内部的引用计数就加一。每析构一次,就减一。当引用计数为0时,删除指向的堆内存。

C++11提供三种智能指针,头文件为<memory>

  • std::shared_ptr:共享的智能指针。
  • std::unique_ptr:独占的智能指针。
  • std::weak_ptr:若引用的智能指针,它不共享指针,不能操作资源,而是用来监视shared_ptr的。

1. shared_ptr的初始化

共享智能指针是指可以有多个智能指针(普通指针不计数)同时管理同一块有效的内存。shared_ptr是一个模板类,有三种初始化方法:

  • 通过构造函数
  • std::make_shared辅助函数
  • reset方法

如果想要查看有多少智能指针同时管理者某一块内存,可以使用共享智能指针的成员函数use_count,原型如下:

long use_count() const noexcept;

1.1 通过构造函数初始化

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

int main(void){
    shared_ptr<int> ptr1(new int(1));
    cout << "ptr1.use_cout()==" << ptr1.use_count() << endl;
    shared_ptr<char> ptr2(new char[3]);
    cout << "ptr2.use_cout()==" << ptr2.use_count() << endl;
    shared_ptr<char[]> ptr3(new char[3]);
    cout << "ptr3.use_cout()==" << ptr3.use_count() << endl;
    shared_ptr<int> ptr4;
    cout << "ptr4.use_cout()==" << ptr4.use_count() << endl;
    shared_ptr<int> ptr5(nullptr);
    cout << "ptr5.use_cout()==" << ptr5.use_count() << endl;
    return 0;
}

输出:

ptr1.use_cout()==1
ptr2.use_cout()==1
ptr3.use_cout()==1
ptr4.use_cout()==0
ptr5.use_cout()==0

结论:

  • 如果智能指针被初始化了一块有效内存,那么该内存的引用计数+1,如果智能指针没有被初始化或者被初始化为nullptr空指针,引用计数不会+1。
  • 不要用一个原始指针初始化多个共享指针,这回导致原始指针的引用计数无法+1。

1.2 通过拷贝构造和移动构造初始化

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

int main(void){
    shared_ptr<int> ptr1(new int(1));
    cout << "ptr1.use_cout()==" << ptr1.use_count() << endl;
    shared_ptr<int> ptr2(ptr1);         	// 拷贝构造
    cout << "ptr2.use_cout()==" << ptr2.use_count() << endl;
    shared_ptr<int> ptr3(move(ptr1));       // 移动构造
    cout << "ptr1.use_cout()==" << ptr1.use_count() << endl;
    cout << "ptr3.use_cout()==" << ptr3.use_count() << endl;
    return 0;
}

输出:

ptr1.use_cout()==1
ptr2.use_cout()==2
ptr1.use_cout()==0
ptr3.use_cout()==2

分析:

  • 第9行:通过拷贝构造初始化共享智能指针,ptr1ptr2的引用计数都+1。
  • 第10行:通过移动构造初始化共享智能指针,此时ptr1的管理的内存被转移给ptr3管理,所以ptr1的引用计数归0,而ptr3的引用计数和ptr1原本的引用计数一样。

1.3 通过std::make_shared初始

C++11提供std::make_shared方法,用于完成内存对象的创建,并将创建好的内存对象初始化给智能指针。函数原型如下:

template <class T, class ... Args>
shared_ptr<T> make_shared(Args&&.... args);

EG:

  • 代码:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    class Test {
    private:
        int num;
    public:
        Test() {
            cout << "non-parameter constructor" << endl;
        }
        Test(int n): num(n) {
            cout << "parameterized constructor" << endl;
        }
        ~Test() {
            cout << "destructor" << endl;
        }
        void setValue(int n) {
            num = n;
        }
        void print(){
            cout << "Test.num==" << num << endl;
        }
    };
    
    int main(void){
        shared_ptr<int> ptr1 = make_shared<int>(100);
        cout << "ptr1.use_count()==" << ptr1.use_count() << endl;
        shared_ptr<Test> ptr2 = make_shared<Test>();
        cout << "ptr2.use_count()==" << ptr2.use_count() << endl;
        shared_ptr<Test> ptr3 = make_shared<Test>(100);
        cout << "ptr3.use_count()==" << ptr3.use_count() << endl;
        return 0;
    }
    
  • 输出:

    ptr1.use_count()==1
    non-parameter constructor
    ptr2.use_count()==1
    parameterized constructor
    ptr3.use_count()==1
    destructor
    destructor
    
  • 分析:

    使用std::make_shared()模板函数可以完成内存地址的创建,并将最终得到的内存地址传递给共享智能指针对象管理。且在申请内存的同时,可以调用对应的构造参数对对象进行初始化。

1.5 使用reset方法进行初始化

共享智能指针提供成员函数reset来对自身进行初始化。

原型:

void reset() noexcept;

template <class Y>
void reset(Y* ptr);

template <class Y, class Deleter>
void reset(Y* ptr, Deleter d);

template <class Y, class Deleter, class Alloc>
void reset(Y* ptr, Deleter d, Alloc alloc);

其中:

  • ptr:指向要获取所有权的对象的指针。
  • d:删除器,指向要获取所有权的对象的指针。
  • alloc:内存存储所用的分配器。

作用:

有两种作用:

  • 放弃当前共享智能指针对内存的指向。

    ptr1.reset();
    
  • 转移共享智能指针的指向:

    shared_ptr<int> ptr1(new int(100));
    ptr1.reset(new int(200));			// 
    

EG:

  • 代码:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    int main(void){
        shared_ptr<int> ptr1 = make_shared<int>(100);
        cout << "ptr1.use_count()==" << ptr1.use_count() << endl;
        shared_ptr<int> ptr2 = ptr1;
        cout << "ptr2.use_count()==" << ptr2.use_count() << endl;
        shared_ptr<int> ptr3 = ptr1;
        cout << "ptr3.use_count()==" << ptr3.use_count() << endl;
        cout << endl;
        ptr3.reset();
        cout << "ptr1.use_count()==" << ptr1.use_count() << endl;
        cout << "ptr2.use_count()==" << ptr2.use_count() << endl;
        cout << "ptr3.use_count()==" << ptr3.use_count() << endl;
        cout << endl;
        ptr2.reset(new int(200));
        cout << "ptr1.use_count()==" << ptr1.use_count() << endl;
        cout << "ptr2.use_count()==" << ptr2.use_count() << endl;
        cout << "ptr3.use_count()==" << ptr3.use_count() << endl;
        return 0;
    }
    
  • 输出:

    ptr1.use_count()==1
    ptr2.use_count()==2
    ptr3.use_count()==3
    
    ptr1.use_count()==2
    ptr2.use_count()==2
    ptr3.use_count()==0
    
    ptr1.use_count()==1
    ptr2.use_count()==1
    ptr3.use_count()==0
    

1.5 操作内存

在使用共享智能指针时,如果想要操作指向的目标,有两种方式:

  • 通过成员函数get()方法获取原始指针,用原始指针来操作。get的函数原型如下:

    T* get() const noexcept;
    
  • 把共享智能指针当作原始指针,直接操作。(实际上是在共享智能指针类内部进行了操作符重载)

EG:

  • 代码:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    class Test {
    private:
        int num;
    public:
        Test() {
            cout << "non-parameter constructor" << endl;
        }
        Test(int n): num(n) {
            cout << "parameterized constructor" << endl;
        }
        ~Test() {
            cout << "destructor" << endl;
        }
        void setValue(int n) {
            num = n;
        }
        void print(){
            cout << "Test.num==" << num << endl;
        }
    };
    
    int main(void) {
        shared_ptr<Test> ptr1 = make_shared<Test>(100);
        Test* p = ptr1.get();
        p.print();
        
        ptr1->setValue(200);
        ptr1->print();
        return 0;
    }
    
  • 输出:

    Test.num==100
    Test.num==200
    

2. 指定删除器

2.1 自定义删除器

删除器:

当智能指针管理的内存对应的引用计数为0时,这块内存会被智能指针析构掉。我们可以在初始化智能指针时自己指定删除动作,也就是删除器,其本质上是一个函数。我们只需要实现该函数,器调用由智能指针完成。

EG:

  • 代码:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    void deletor(int *p){
        cout << "custom deletor" << endl;
        delete p;
    }
    
    int main(void) {
        shared_ptr<Test> ptr1(new int(100), deletor);
        return 0;
    }
    
  • 输出:

    custom deletor
    

2.2 lambda 删除器

可以使用lambda表达式作为删除器函数:

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

int main(void) {
    shared_ptr<char> ptr(new char('a'),
                         [](char *p){
                             cout << "lambda deletor" << endl;
                             delete p;
                         })
    return 0;
}

输出:

lambda deletor

2.3 数组删除器

在较早版本的C++11中,std::shared_ptr的默认删除器不支持数组对象,如:

class Test {
public:
    Test() { cout << "constructor" << endl; }
    ~Test() { cout << "destructor" << endl; }
};
shared_ptr<Test> ptr(new Test[5]); 

这会导致报错:

constructor
constructor
constructor
constructor
constructor
destructor
段错误 (核心已转储)

明明有五个对象被创建了,却只析构了一个对象,因此内存泄漏,出现段错误。

面对这种问题,有两种解法:

  • 在声明智能指针模板类型时,使用数组

    class Test {
    public:
        Test() { cout << "constructor" << endl; }
        ~Test() { cout << "destructor" << endl; }
    };
    shared_ptr<Test[]> ptr(new Test[5]);	// 这里将模板参数声明为数组类型 
    
  • 定义数组删除器。又有两种方式:

    • 自定义删除器:

      class Test {
      public:
          Test() { cout << "constructor" << endl; }
          ~Test() { cout << "destructor" << endl; }
      };
      shared_ptr<Test> ptr(new Test[5], 
                           [](Test* p){
                               delete[] p;
                           });	
      
    • 使用C++提供的std::default_delete<T>()函数作为删除器。该函数内部逻辑也是通过delete来实现的,要释放什么类型的类型,就指定什么类型的模板参数T

      class Test {
      public:
          Test() { cout << "constructor" << endl; }
          ~Test() { cout << "destructor" << endl; }
      };
      shared_ptr<Test> ptr(new Test[5], default_delete<Test[]>());	
      

标签:use,cout,ptr1,智能,shared,共享,ptr,指针
From: https://www.cnblogs.com/beasts777/p/17896959.html

相关文章

  • 独占智能指针
    文章参考:爱编程的大丙(subingwen.cn)1.初始化:特点:相较于共享智能指针,独占智能指针(unique_ptr)的关键在于:同一时刻,只能有一个智能指针指向同一块内存。因此独占智能指针不允许使用拷贝构造函数和拷贝赋值函数。初始化:通过构造函数初始化:unique_ptr<int>ptr1(newint(1......
  • 聊天记录年度报告一览无余:轻松多格式导出永久保存,深度智能分析
    聊天记录年度报告一览无余:轻松多格式导出永久保存,深度智能分析1.功能简介效果展示一个用于提取微信聊天记录的工具,支持将聊天记录导出成HTML、Word、CSV文档,以实现永久保存。此外,该工具还具有对聊天记录进行分析的功能,可以生成年度聊天报告,帮助用户更好地了解和回顾与他人的沟通......
  • CodeGeeX智能编程
    一、写在前面大家遇到代码不会的问题,本能的就会去求助chatGPT,但是没有梯子的话,chatGPT是不是也帮不上忙了?秉着白嫖的精神,分享给大家一款非常牛的插件CodeGeex。二、CodeGreex简介CodeGreex支持多种主流IDE,如VSCode、IntelliJIEAD、PyCharm、vim等,同时支持Python、java、C++/C......
  • 计算机初级选手的成长历程——指针(5)
    进阶指针导言大家好,很高兴又和大家见面了!!!在上一个章节中,咱们深入探讨了一下指针与数组之间的联系,在探讨的过程中我们发现对于指针数组与二级指针来说,它们实质上就是一维数组和一级指针,它们之间的关系也是遵从指针与数组之间关系。为了更好的学习指针,在今天的内容中,我们将介绍指针的......
  • 探索AI在CRM中的潜力:智能化客户关系的构建
    AI人工智能在CRM系统中的应用有:赋能内容生产、客户服务支持、赋能品牌推广、自动化业务流程、数据分析、辅助科学决策、给出最佳客户联系时间。合理运用CRM系统中AI人工智能助手可以让团队工作事半功倍。1.内容生产市场营销活动离不开内容生产,持续的产出高质量的内容又给营销团......
  • 火星探测器背后的人工智能:从原理到实战的强化学习
    本文详细探讨了强化学习在火星探测器任务中的应用。从基础概念到模型设计,再到实战代码演示,我们深入分析了任务需求、环境模型构建及算法实现,提供了一个全面的强化学习案例解析,旨在推动人工智能技术在太空探索中的应用。关注TechLead,分享AI全维度知识。作者拥有10+年互联网服务......
  • 【开源项目推荐】-支持GPT的智能数据库客户端与报表工具——Chat2DB
    2023年是人工智能爆火的一年,ChatGPT为首的一系列的大模型的出现,让生成式人工智能彻底火了一把。但有人会说,GPT对于我们数据开发来说并没有什么作用啊?今天为大家推荐的开源项目,就是GPT在数据领域的一个优秀实践项目。让我们一起来看看吧~Chat2DB是一个集成了ChatGPT功能的数据库S......
  • 多开工具与智能城市建设的结合与创新
    多开工具与智能城市建设的结合与创新摘要:随着科技的快速发展,智能城市建设成为了现代化城市发展的重要目标。而多开工具作为一种技术手段,不仅可以提高城市基础设施的效率,还能够为智能城市建设带来更多的创新。本文将探讨多开工具与智能城市建设的结合,并提出一些创新的观点和建议。......
  • Samba共享服务搭建
    环境准备主机IPRocky_Linux(samba服务器)192.168.3.1/24windows11(客户端)192.168.3.25/24安装服务dnfisntallsamba-y修改配置创建共享目录登录用户useradd-M-s/sbin/nologinqclrecho123|passwd--stdinqclrmkdir/opt/qclrchown-Rqclr:qclrqclr/smbpasswd-aqclr......
  • 实验6 C语言结构体,枚举应用编程(附实验5 C语言指针应用编程)
    实验6一,实验目的二,实验准备三,实验内容1,实验任务1task1.c1#include<stdio.h>2#include<string.h>3#defineN3//运行程序输入测试时,可以把这个数组改小一些输入测试45typedefstructstudent{6intid;//学号7......