首页 > 编程语言 >C++11之智能指针shared_ptr

C++11之智能指针shared_ptr

时间:2023-03-27 19:44:28浏览次数:44  
标签:11 std p1 C++ info shared ptr 指针

  在 C++ 开发中,我们经常会遇到程序运行中突然崩溃、程序运行所用内存越来越多最终不得不重启等问题,这些问题往往都是内存资源管理不当造成的。C++11 新标准中,增添了 unique_ptr、shared_ptr 以及 weak_ptr 这 3 个智能指针来实现堆内存的自动回收,今天就简单的介绍一下shared_ptr的使用。

  C++ 智能指针底层是采用引用计数的方式实现的。智能指针在申请堆内存空间的同时,会为其配备一个整形值(初始值为 1),每当有新对象使用此堆内存时,该整形值 +1;反之,每当使用此堆内存的对象被释放时,该整形值减 1。当堆空间对应的整形值为 0 时,即表明不再有对象使用它,该堆空间就会被释放掉。多个 shared_ptr 智能指针可以共同使用同一块堆内存。并且,由于该类型智能指针在实现上采用的是引用计数机制,即便有一个 shared_ptr 指针放弃了堆内存的“使用权”(引用计数减 1),也不会影响其他指向同一堆内存的 shared_ptr 指针(只有引用计数为 0 时,堆内存才会被自动释放)。

  从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。

一、智能指针shared_ptr<T>的初始化

#ifndef SHAREPTR_H
#define SHAREPTR_H

#include <QObject>
#include <QDebug>

class SharePtr : public QObject
{
    Q_OBJECT
public:
    explicit SharePtr(QString info, QObject *parent = nullptr);
    ~SharePtr();

public:
    QString& get_info();
    void set_info(QString info);
    void print_info();

private:
    QString m_info;
};

#endif // SHAREPTR_H
#include "share_ptr.h"

SharePtr::SharePtr(QString info, QObject *parent) : QObject(parent),m_info(info)
{
    QString test = QString("%1被创造").arg(info);
    qDebug() << test;
}


SharePtr::~SharePtr()
{
    qDebug() << "SharePtr delete:" << m_info;
}

QString& SharePtr::get_info()
{
    return m_info;
}

void SharePtr::set_info(QString info)
{
    m_info = info;
}

void SharePtr::print_info()
{
    qDebug() << "info:" << m_info;
}
#include <QCoreApplication>
#include <QDebug>

#include "share_ptr.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    /* 通过如下2种方式,可以构造出 shared_ptr<T> 类型的空智能指针 */
    std::shared_ptr<SharePtr> p1;             /* 不传入任何实参 */
    /* 注意,空的shared_ptr指针,其初始引用计数为 0,而不是 1。 */
    qDebug() << "p1.count:" << p1.use_count();
    std::shared_ptr<SharePtr> p2(nullptr);    /* 传入空指针 nullptr */
    qDebug() << "p2.count:" << p2.use_count();

    /* C++11 标准中提供了 std::make_shared<T> 模板函数,其可以用于初始化 shared_ptr智能指针 */
    std::shared_ptr<SharePtr> share_ptr = std::make_shared<SharePtr>("123");
    /* 查看资源的所有者个数 */
    qDebug() << "share_ptr.count:" << share_ptr.use_count();
    share_ptr.get()->print_info();

    /* 在构建 shared_ptr智能指针,也可以明确其指向 */
    std::shared_ptr<SharePtr> share_ptr1(new SharePtr("456"));
    qDebug() << "share_ptr1.count:" << share_ptr1.use_count();
    share_ptr1.get()->print_info();

    return a.exec();
}

打印结果如下:

 二、shared_ptr<T> 模板还提供有相应的拷贝构造函数和移动构造函数

#include <QCoreApplication>
#include <QDebug>

#include "share_ptr.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    /* 调用拷贝构造函数 */
    /* p 和 p1 都是 shared_ptr 类型的智能指针,因此可以用 p 来初始化 p1,由于 p3 是左值,
     *因此会调用拷贝构造函数。需要注意的是,如果 p 为空智能指针,则 p1 也为空智能指针,其引用计数初始值为 0;反之,
     * 则表明 p 和 p1 指向同一块堆内存,同时该堆空间的引用计数会加 1。 */
    std::shared_ptr<SharePtr> p = std::make_shared<SharePtr>("111");
    qDebug() << "p.count:" << p.use_count();
    std::shared_ptr<SharePtr> p1(p); /* 或者 std::shared_ptr<SharePtr> p1 = p; */
    qDebug() << "p1.count:" << p1.use_count();

    /* 调用移动构造函数  */
    std::shared_ptr<SharePtr> p2(std::move(p1)); /* 或者 std::shared_ptr<SharePtr> p2 = std::move(p1); */
    /* 对于 std::move(p1) 来说,该函数会强制将 p1 转换成对应的右值,因此初始化 p2 调用的是移动构造函数。
     * 另外和调用拷贝构造函数不同,用 std::move(p1) 初始化 p2,会使得 p2 拥有了 p1 的堆内存,而 p1 则变成了空智能指针*/
    qDebug() << "p2.count:" << p2.use_count();
    qDebug() << "p1.count:" << p1.use_count();

    std::shared_ptr<SharePtr> p3 = std::make_shared<SharePtr>("222");
    qDebug() << "p3.count():" << p3.use_count();
    std::shared_ptr<SharePtr> p4 = std::make_shared<SharePtr>("333");
    qDebug() << "p4.count():" << p4.use_count();

    p3 = p4; /* "333"引用计数加1,"222"销毁 */
    p3.get()->print_info();
    qDebug() << "p3.count():" << p3.use_count();
    qDebug() << "p4.count():" << p4.use_count();
    p3.reset();
    qDebug() << "p3.count():" << p3.use_count();
    p4.reset();/* 引用计数为0,此时"333"销毁 */

    return a.exec();
}

 打印结果如下:

 三、自定义智能指针的释放规则

  在初始化 shared_ptr 智能指针时,还可以自定义所指堆内存的释放规则,这样当堆内存的引用计数为 0 时,会优先调用我们自定义的释放规则。在某些场景中,自定义释放规则是很有必要的。

比如,对于申请的动态数组来说,shared_ptr 指针默认的释放规则是不支持释放数组的,只能自定义对应的释放规则,才能正确地释放申请的堆内存。

对于申请的动态数组,释放规则可以使用 C++11 标准中提供的 default_delete<T> 模板类,我们也可以自定义释放规则:

#include <QCoreApplication>
#include <memory>

/* 自定义释放规则 */
void delete_int(int*p) 
{
    delete []p;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    /* 指定 default_delete 作为释放规则 */
    std::shared_ptr<int> p(new int[10], std::default_delete<int[]>());
      
    /*初始化智能指针,并自定义释放规则 */
    std::shared_ptr<int> p1(new int[10], delete_int);
    
    
    return a.exec();
}

标签:11,std,p1,C++,info,shared,ptr,指针
From: https://www.cnblogs.com/QingYiShouJiuRen/p/17262616.html

相关文章

  • 11、短语
    2023/3/27短语theNorthAtlantic,北大西洋fromendtoend,从头到尾、从这端到另一端haveeffecton...,对······产生影响theglobalclimate,全球气候powerpl......
  • 1111
    ......
  • C++ primer第十五章总结
    1oop的思想是数据抽象继承和动态绑定数据抽象可以将类的接口与实现分离;继承动态绑定,又称运行时绑定2虚函数是基类希望其派生类进行覆盖的函数<1>任何构造函......
  • C++学习路线
    C++是一种高级编程语言,广泛用于开发操作系统、应用程序、游戏和各种工具。如果你想学习这门语言,以下是一个适合初学者的学习路线:第一步:学习C++基础知识在学习C++之前,你需......
  • C++ stringstream ssin 的用法
    C++中stringstream方法存在于头文件<sstream>中作用:使用stringstream方法,将某一字符串生成输入流,然后可以利用这个输入流把长的整行字符串转换成单个字符#include......
  • LeetCode 111.二叉树的最小深度
    1.题目给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。说明:叶子节点是指没有子节点的节点。示例1:输入:root=[3,9,20,null,null......
  • CSG1140 -- 括号序列
    括号序列分析:线段树维护区间(与)的差值:首先若两个位置是相同字符,不会改变匹配形式,直接YES;若选择)(改变,因为原本是合法的,这样交换过后总是能再次匹配;若选择()改变......
  • C++ STL标准库 迭代器相关
    迭代器是什么及用法详解[迭代器是什么及用法详解C语言中文网](http://c.biancheng.net/view/6675.html)迭代器是C++STL(标准模板库)中一种非常重要的概念,它......
  • Essential C++学习(一)
    今天是新人第一天写博客,很多东西都不太会,希望各位见谅。文章可能有很多幼稚的看法,在此提前感谢大佬指正。 很多作者都没有特别关注第一章的内容,但就在写这篇随......
  • MAC 装 Oracle JDeveloper 11g方法
    1去苹果官方网站下载支持你系统JAVA,Lion系统需要单独安装,雪豹自带JAVA,只需要更新新版即可地址是这个http://support.apple.com/downloads/2如果你的帐号不是管理员以及......