C++11智能指针

为了解决C++内存泄漏的问题,C++11引入了智能指针(SmartPointer)。智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,C++中有一个重要原则,在函数结束

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

  为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer)。

  智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,C++中有一个重要原则,在函数结束时(不论是正常返回,还是因为异常除法的对战回退),会将所有栈对象销毁,也就是会调用所有栈对象的析构函数。智能指针内部保存的内存也就被释放掉了(除非将智能指针保存起来)。

  C++11提供了三种智能指针:std::shared_ptr, std::unique_ptr, std::weak_ptr,使用时需添加头文件<memory>。

  shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,删除所指向的堆内存。shared_ptr内部的引用计数是安全的,但是对象的读取需要加锁。

1. shared_ptr

1.1 基本用法

(1)初始化

  可以通过构造函数、std::make_shared<T>辅助函数和reset方法来初始化shared_ptr

#include "stdio.h"
#include<memory>
#include<iostream>
using namespace std;

void main()
{
    shared_ptr<int> p1(new int(10));
    cout << "p1的引用计数:"<<p1.use_count() << endl;
    shared_ptr<int> p2 = p1; 
    cout << "p1的引用计数:" << p1.use_count() << " p2的引用计数:" << p2.use_count() << endl;
    p2.reset(new int(2));   // 当智能指针中有值时,调用rest会使引用计数减1
    cout << "p1的引用计数:" << p1.use_count() << " p2的引用计数:" << p2.use_count() << endl;
    cout << *p2 << endl;
    p2.reset(); 
    cout << "p1的引用计数:" << p1.use_count() << " p2的引用计数:" << p2.use_count() << endl;

    shared_ptr<int> p3 = make_shared<int>(20); 
    shared_ptr<int> p4;
    p4.reset(new int(30));
    cout << "p4的引用计数:" << p4.use_count() << endl;
}

注意:不能将一个原始指针直接赋值给一个智能指针

std::shared_ptr<int> p4 = new int(1);// error

  reset()包含两个操作。当智能指针中有值的时候,调用reset()会使引用计数减1.当调用reset(new xxx())重新赋值时,智能指针首先是生成新对象,然后将就对象的引用计数减1(当然,如果发现引用计数为0时,则析构旧对象),然后将新对象的指针交给智能指针保管。

(2)获取原始指针

std::shared_ptr<int> p4(new int(5));
int *pInt = p4.get();

(3)指定删除器

  智能指针可以指定删除器,当智能指针的引用计数为0时,自动调用指定的删除器来释放内存。std::shared_ptr可以指定删除器的一个原因是其默认删除器不支持数组对象,这一点需要注意。

template<class T, class D>
shared_ptr(T *p, D d)
void DeleteIntPtr(int *p)
{
     delete p;      
}

shared_ptr<int> p(new int(1), DeleteIntPtr);

  当我们使用shared_ptr管理动态数组时,需要指定删除器,因为std::shared_ptr的默认删除器不支持数组对象

shared_ptr<int> p(new int[10], [](int *p){delete[] p;});

  也可以使用syd::default_delete作为删除器,default_delete的内部是通过调用delete来实现的

shared_ptr<int> p(new int[10], deault_delete<int[]>);

(4)注意make_share和make_arrar_share的使用

(5)使用shared_ptr注意事项

a. 不要用一个原始指针初始化初始化多个shared_ptr

b. 不要在函数实参中创建shared_ptr

2. unique_ptr独占指针

  unique_ptr是一个独占型的智能指针,它不允许其它的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另外一个unique_ptr,unique_ptr不允许复制,但可以通过函数返回给其它的unique_ptr,还可以通过move来转移到其它的unique_ptr,这样它本身就再拥有原来指针的所有权。

注意:unique_ptr不支持make_share

3. weak_ptr弱引用指针

  弱引用指针week_ptr是用来监视shared_ptr的,不会使引用计数加1,它不管理shared_ptr内部的指针,week_ptr没有重载操作符,不共享指针,不操作资源。weak_ptr指针可以用来返回this指针和解决循环引用的问题

(1)use_count获取当前观测资源的引用计数

shared_ptr<int> p(new int(1));
weak_ptr<int> p1(p);
cout << p1.use_count() <<endl;

(2)通过expired()来判断观测的资源是否已经被释放

(3)通过lock()来获取监视的shared_ptr

(4)weak_ptr返回this指针

  不能直接将this指针返回为shared_ptr,需要通过派生std::enable_shared_from_this类,并通过其方法shared_from_this来返回智能指针,原因是enable_shared_from_this类中有一个weak_ptr指针,这个weak_ptr用来观测this智能指针,调用shared_from_this,会调用内部的weak_ptr的lock()方法

(5 )weak_ptr解决循环引用的问题

 

#include "stdio.h"
#include<memory>
#include<iostream>
using namespace std;

class B;
class A
{
public:
    shared_ptr<B> bptr;
    ~A()
    {
        cout << "A is delete!" << endl;
    }
};

class B
{
public:
    shared_ptr<A> aptr;
    ~B()
    {
        cout << "B is delete!" << endl;
    }
};
void main()
{
    {
        shared_ptr<A> a(new A());
        shared_ptr<B> b(new B());

        a->bptr = b;
        b->aptr = a;

        cout << "A的引用计数:" << a.use_count() << endl;
        cout << "B的引用计数:" << b.use_count() << endl;
    }

    int i = 0;
}

C++11智能指针

离开作用域后引用计数减1,不会去删除指针,导致内存泄漏

通过weak_ptr解决该问题,只要将A或B中的任意一个成员改成weak_ptr即可

C++11智能指针

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/120118.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • L3-023 计算图(链式求导+bfs拓扑|dfs)「建议收藏」

    L3-023 计算图(链式求导+bfs拓扑|dfs)「建议收藏」原题链接“计算图”(computational graph)是现代深度学习系统的基础执行引擎,提供了一种表示任意数学表达式的方法,例如用有向无环图表示的神经网络。 图中的节点表示基本操作或输入变量,边表示节点之间的中间值的依赖性。 例如,下图就是一个函数 ( 的计算图。现在给定一个计算图,请你根据所有输入变量计算函数值及其偏导数(即梯度)。 例如,给定输入,,上述计算图获得函数值 (;并且根据微分链式法则,上图得到的梯度 ∇。知道你已经把微积分忘了,所以这里只要求你处理几个简单的算子:加法、减法、乘

  • 秒杀多线程第六篇 经典线程同步 事件Event

    秒杀多线程第六篇 经典线程同步 事件Event阅读本篇之前推荐阅读以下姊妹篇:《秒杀多线程第四篇一个经典的多线程同步问题》《秒杀多线程第五篇经典线程同步关键段CS》 上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权”特性所以关键段只能用于线程的互斥而不能用于同步。本篇介绍用事件Event来尝试解决这个线程同步问题。首先介绍下如何使用事件。事件Event实际上是个内核对象,它的使用非常方便。下面列出一些常用的函数。

  • 「 运动控制 」“ADRC自抗扰控制技术”(Active Disturbance Rejection Control)研究

    「 运动控制 」“ADRC自抗扰控制技术”(Active Disturbance Rejection Control)研究近年来,虽然现代控制理论取得了一系列成果,但是在工业领域的应用并没有代替PID控制。这说明现代控制理论在实际应用的过程中受到一定的限制,控制理论与生产生活实际仍然存在一定的代沟。对此,韩京清总结为基于“模型论”的控制理论在解决问题时没有以生产生活中的不确定性作为重点,没有抓住生产生活实践中所切实需要的核心。韩京清先生针对生产生活实践中存在的问题潜心研究,积极探索,逐步分析PID理论与现代控…

  • MySQL 约束条件[通俗易懂]

    MySQL 约束条件[通俗易懂]主键(PRIMARYKEY)标识该属性为该表的主键,可以唯一的标识对应的记录。外键(FOREIGNKEY)标识该属性为该表的外键,与某个表的主键关联。唯一性(UNIQUE)标识该属性的值是唯一的。非空(NOTNULL)标识该属性不能为空。默认值(DEFAULT)为该属性设置默认值。*MySQL不支持CHECK约束,但可以使用CHECK约束而没

  • 学习Java的9张思维导图

    学习Java的9张思维导图红包网上搜集了java的学习思维导图,分享给大家。01.Java程序设计(基础)02.Java程序设计(专题)03.客户端网页编程04.JavaWeb表示层技术05.Oracle06.Hibernate07.MyBATIS08.Spring还有一幅java基础知识思维导图:具体忘记从哪里找到的这么好的资料,谢谢原作者…

  • nchar,char,varchar与nvarchar区别「建议收藏」

    nchar,char,varchar与nvarchar区别「建议收藏」nchar,char,varchar与nvarchar区别

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号