找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
  • 网友反映远安桃花岛步道破损隐患,住建部门火速处置,官方留言却遭平台屏蔽引唏嘘

    网友反映远安桃花岛步道破损隐患,住建部门

  • 网曝远安桃花岛上惊现安全隐患,钉子都出来了,周边都是孩子在玩耍

    网曝远安桃花岛上惊现安全隐患,钉子都出来

  • 被网友举报远安北万路上这辆挂人后视镜直接走掉的鄂A本田,罚单来了

    被网友举报远安北万路上这辆挂人后视镜直接

  • 网友举报这辆半挂竟公然在远安泉水冲那里闯红灯,别急,罚单已到位!

    网友举报这辆半挂竟公然在远安泉水冲那里闯

  • 好消息!武陵峡果园杨梅开园啦!老板发福利啦

    好消息!武陵峡果园杨梅开园啦!老板发福利

  • 精装修门面出租:无转让费!周边配套齐全,房东(胡)

    精装修门面出租:无转让费!周边配套齐全,

  • 厕所顶部漏水半年多维权困难 寻求帮助

    厕所顶部漏水半年多维权困难 寻求帮助

  • 远安G347国道上这辆大货车,你的罚单来了

    远安G347国道上这辆大货车,你的罚单来了

  • 紧急提醒:远安县刚刚发布暴雨橙色预警,注意避险防范

    紧急提醒:远安县刚刚发布暴雨橙色预警,注

  • 【招聘保洁熟手】

    【招聘保洁熟手】

  • 襄阳发布警情通报

    襄阳发布警情通报

  • 吐槽一下家门口的清洗街道车😔😔

    吐槽一下家门口的清洗街道车😔😔

  • 险象环生!远安万山弯道五菱神车逆行别车,罚单来了

    险象环生!远安万山弯道五菱神车逆行别车,

  • 突发!刚刚远安这条网红彩虹路边,翻了一辆车

    突发!刚刚远安这条网红彩虹路边,翻了一辆

  • 远安凤凰湾门口路段这位女司机,你的罚单来了

    远安凤凰湾门口路段这位女司机,你的罚单来

  • 【小董有个友好请求🫡】

    【小董有个友好请求🫡】

  • 一串红提,宛如秋日里的一抹亮色...

    一串红提,宛如秋日里的一抹亮色...

  • 远安汽摩协会与远安特校十年相伴,童心同行

    远安汽摩协会与远安特校十年相伴,童心同行

  • 【五月·共勉】

    【五月·共勉】

  • 当阳市公安局玉泉派出所原指导员朱心维接受纪律审查和监察调查

    当阳市公安局玉泉派出所原指导员朱心维接受

  • 远安广坪路段这辆奥迪,你的行为太危险了,你就不怕成了夹心饼干吗

    远安广坪路段这辆奥迪,你的行为太危险了,

  • 在远安洋坪路段违法超车的这辆车,你的罚单刚刚出来了

    在远安洋坪路段违法超车的这辆车,你的罚单

  • 这辆车在远安县解放路与凤德路交汇路口违停,极大交通隐患

    这辆车在远安县解放路与凤德路交汇路口违停

  • 网友反映远安城区这里的行道树上长满了白蚁,观之肉麻!不知道该找谁治

    网友反映远安城区这里的行道树上长满了白蚁

  • 【上午9点把门锁, 小飞侠这是要闹哪出? 】

    【上午9点把门锁, 小飞侠这是要闹哪出? 】

  • 湖北安广陶瓷招聘销售跟单

    湖北安广陶瓷招聘销售跟单

  • 今晚,调油价!

    今晚,调油价!

  • 必须曝光!今天上午,远安嫘祖镇广坪路段这辆婚庆公司的轻卡强行超车肇事逃逸

    必须曝光!今天上午,远安嫘祖镇广坪路段这

  • 坚强的小米辣

    坚强的小米辣

  • 远安人,这个“2026年度综合补贴”是诈骗!

    远安人,这个“2026年度综合补贴”是诈骗!

  • 查看: 9482|回复: 3

    C++(浅析)智能指针及C#GC(垃圾回收机制)分析[图]

    [复制链接]
    • 打卡等级:无名新人
    • 打卡总天数:1
    • 打卡月天数:0
    • 打卡总奖励:10
    • 最近打卡:2023-06-13 20:58:02
         
    荆楚不肖生 发表于 2018-8-6 19:37 | 显示全部楼层 |阅读模式 来自 中国–湖北–宜昌 电信

    赶紧注册,享受更多功能!查看帖内大图!

    您需要 登录 才可以下载或查看,没有账号?立即注册

    ×
    C++(浅析)智能指针及C#GC(垃圾回收机制)分析[图]
    c++中我们常常使用运算符new和delete来分配和释放动态内存,然而动态内存的管理非常容易出错
    使用new 和delete 管理内存存在三个常见问题:
    1.忘记delete(释放) 内存。(或者异常导致程序过早退出,没有执行 delete)忘记释放动态内存会导致人们常说的 内存泄露 问题,你申请了内存而为归还给操作系统长时间这样会导致系统内存越来越小。
    (内存泄露问题往往很难查找到,内存耗尽时,才能检测出这种错误)
    2.使用已经释放掉的对象。比如:我们使用delete释放掉申请的内存空间,但并未干掉指向这片空间的指针,此时指针指向的就是“垃圾”内存。
    3.同一块内存释放两次。当有两个指针指向相同的动态内存分配对象时,其中一个进行了delete操作 对象内存就还给了操作系统 ,如果我们要delete第二个指针,那么内存有可能遭到破坏(浅拷贝问题)
    为了更容易同时也更安全的使用动态内存标准库为我们提供了 智能指针 来管理动态对象,智能指针的行为类似常规的指针,但是它并非指针,它是一个存储指向动态分配(堆)对象指针的类。

    C++(浅析)智能指针及C#GC(垃圾回收机制)分析[图]

    C++(浅析)智能指针及C#GC(垃圾回收机制)分析[图]
    这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针(释放管理的堆空间)。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放
    因为智能指针行为类似于指针 所以一般的指针行为智能指针也具备(每个智能指针重载了->和 *)
    T& opertaor*(){
    return *_ptr;   
    } //这里返回了一个对象T 我们需要对T进行改动 所以返回T&
    T* operator->(){
    return _ptr;
    } //这里相当于是返回了一个指针,然后在使用这个指针指向一个内容
    话不多说首先让我们来看第一种智能指针auto_ptr(管理权限转让)
    我们经常会遇到这种问题比如说浅拷贝的时候多个指针管理空间 但是当其中一个指针先结束释放了这块空间 ,那么当其他指针结束时在对这片空间进行释放 程序就会崩溃。
    为了方便解决上述问题我就可以使用智能指针auto_ptr 其主要原理是是在构造对象时赋予其管理空间的所有权,在拷贝或赋值中转移空间的所有权 拷贝和赋值后直接将_ptr赋为空,禁止其再次访问原来的内存空间。
    //auto_ptr的简单实现
    template<class T>
    class Autoptr
    {
    public:
    Autoptr(T* ptr=NULL)
    :_ptr(ptr)
    {}
    T* operator->()
    {
    return _ptr;
    }
    T& operator*()
    {
    return *_ptr;
    }
    Autoptr(Autoptr<T>& ap){ //拷贝构造
    this->_ptr = ap._ptr;
    ap._ptr = NULL;
    }
    Autoptr<T>& operator=(Autoptr<T>& ap){
    if (this != &ap){
    delete this->_ptr;
    this->_ptr = ap._ptr;
    ap._ptr = NULL;
    }
    return *this;
    }
    ~Autoptr(){
    if (_ptr){
    cout << "释放空间le" << endl;
    delete _ptr;
    }
    }
    private:
    T* _ptr;
    };
    int main(){
    //Autoptr<int>ap1(new int);
    //*ap1 = 10;
    //Autoptr<int>ap2(ap1);
    //Autoptr<int>ap3(ap2);
    //*ap3 = 20;
    //ap2 = ap3;
    //cout << *ap2 << endl;
    Autoptr<int>ap1(new int);
    *ap1 = 10;
    Autoptr<int>ap2(ap1);
    cout << *ap1 << endl;//调试到这一步程序崩溃了,罪魁祸首就是   AutoPtr<int>ap2(ap1),
    //这里原因就是ap2完全的夺取了ap1的管理权。
    //导致ap1被置为NULL,访问它的时候程序就会崩溃。
    system("pause");
    return 0;
    }
    由于它实现了完全的权限转移,所以导致在拷贝构造和赋值之后只有一个指针可以使用,而其他指针都置为NULL,使用很不方便,而且还很容易对NULL指针进行解引用,导致程序崩溃,其危害也是比较大的。
    为了解决 auto_ptr 带来的问题另一种智能指针横空出世—–scoped_ptr(防拷贝) 它的实现原理是直接将拷贝构造和赋值运算符设置为私有或保护只声明不定义
    防止他人在类外定义,这样一次就只有一个指针对空间进行管理就不会出现上面的问题。
    scopd_ptr ( unique_ptr )
    //Scoped_ptr 独占资源
    template<class T>
    class Scoped_ptr
    {
    public:
    Scoped_ptr(T* _Ptr=NULL)
    :_ptr(ptr)
    {}
    T& operator*(){
    return *_ptr;
    }
    T* operator->(){
    return _ptr;
    }
    ~Scoped_ptr()
    {
    if (_ptr){
    delete _ptr;
    }
    }
    private:
    Scoped_ptr(const Scoped_ptr<T>&);
    //{}
    Scoped_ptr<T>& operator=(Scoped_ptr<T>& ap);
    private:
    T* _ptr;
    };
    //int main(){
    //Scoped_ptr<int> sp1(new int);
    //Scoped_ptr<int> sp2(sp1);  不可以进行拷贝构造 此函数设置为私有 不可进行访问
    //system("pause");
    //return 0;
    //}
    第三种智能指针shared_ptr (引用计数版本)允许多个指针指向同一对象
    其原理是 通过引用计数记录对当前操作的指针的个数当进行拷贝构造或者赋值时_pCount++, 析构时当_pCount为0时才进行释放空间。
    //简单实现
    template<class T>
    class Shared_ptr{
    public:
    Shared_ptr(T* ptr = NUll)
    :_ptr(ptr)
    , _pCount(new int(1))
    {}
    /*{
    if (_ptr){
    _pCount = new int(1);
    }
    }*/
    Shared_ptr(const Shared_ptr<T>& sp)
    :_ptr(sp._ptr)
    ,_pCount(sp._pCount)
    {
    ++GetRef();
    }
    //当进行拷贝或赋值操作时 每个shared_ptr都会有一个计数器 记录着和它指向相同空间的shared_ptr的个数
    Shared_ptr<T>& operator=(const Shared_ptr<T>& sp){
    if (this != &sp)
    {
    Release();
    _ptr = sp._ptr;
    _pCount = sp._pCount;
    ++GetRef();
    }
    return *this;
    }
    T& operator*(){
    return *_ptr;
    }
    T* operator->(){
    return _ptr;
    }
    ~Shared_ptr(){
    Release();
    }//当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类才会自动销毁此对象
    int Usecount(){
    return GetRef();
    }
    private:
    void Release(){
    if (0 == --*_pCount &&_ptr){
    delete _ptr;
    delete _pCount;
    _ptr = NULL;
    _pCount = NULL;
    }
    }
    int& GetRef(){
    return *_pCount;
    }
    private:
    T* _ptr;
    T* _pCount;
    };
    /*int main(){
    Shared_ptr<int> sp1(new int(10));
    Shared_ptr<int> sp2(sp1);
    *sp1 = 10;
    *sp2 = 20;
    cout << sp1.Usecount() << endl;
    cout << sp2.Usecount() << endl;
    Shared_ptr<int> sp3(new int(30));
    Shared_ptr<int> sp4(sp3);
    sp4 = sp2;
    *sp4 = 40;
    cout << sp3.Usecount() << endl;
    cout << sp4.Usecount() << endl;
    system("pause");
    return 0;
    }*/
    看完shared_ptr基本原理 我们在来分析分析它的缺陷(循环引用问题)
    例:
    #include<memory>
    template<class T>
    struct ListNode{
    //ListNode<T>* _next;
    shared_ptr<ListNode<T>> _next;
    //ListNode<T>* _prev;
    shared_ptr<ListNode<T>> _prev;
    T _data;
    ListNode(const T& data = T())
    :_next(NULL)
    , _prev(NULL)
    , _data(data)
    {
    cout << "LisNode(const T& data)" << this << endl;
    }
    ~ListNode(){
    cout << "~ListNode():" << this << endl;
    }
    };
    void Testshared_ptr(){
    shared_ptr<ListNode<int>> p1(new ListNode<int>(10));
    shared_ptr<ListNode<int>> p2(new ListNode<int>(20));
    cout << p1.use_count() << endl;
    cout << p2.use_count() << endl;
    p1->_next = p2;
    p2->_prev = p1;
    cout << p1.use_count() << endl;
    cout << p2.use_count() << endl;
    }
    int main(){
    Testshared_ptr();
    system("pause");
    return 0;
    }
    这里写图片描述
    为了解决shared_ptr中的循环引用问题 我们引入shared_ptr的助手指针 weak_ptr 我们来看看代码怎么改
    #include<memory>
    template<class T>
    struct ListNode{
    //ListNode<T>* _next;
    weak_ptr<ListNode<T>> _next;
    //shared_ptr<ListNode<T>> _next;
    //ListNode<T>* _prev;
    weak_ptr<ListNode<T>> _prev;
    //shared_ptr<ListNode<T>> _prev;
    T _data;
    ListNode(const T& data = T())
    /*:_next(NULL)
    , _prev(NULL)
    , _data(data)*/
    :_data(data)
    {
    cout << "LisNode(const T& data):" << this << endl;
    }
    ~ListNode(){
    cout << "~ListNode():" << this << endl;
    }
    };
    这里写图片描述
    weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。同时,weak_ptr 必须从一个share_ptr或者另一个weak_ptr转换而来,不能使用new 对象进行构造。由于弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用。
    C# GC(垃圾回收机制):
    1,什么是资源:
    所谓的资源就是程序中可利用的数据,譬如:字符串、图片和任何二进制数据,包括任何类型的文件。
    2,访问资源的步骤:
    1)分配内存:分配一定的内存空间。
    2)  初始化内存: 一个类型的实例构造器负责这样的初始化工作。
    3)使用资源: 通过访问类型成员来使用资源。根据需要会有反复。
    4)销毁资源: 执行清理工作。
    什么是托管资源,非托管资源:托管资源是由CLR全权负责的资源,CLR不负责的资源位非托管资源。
    对于托管资源通过GC自动清理回收。对于非托管资源,通过代码调用手动进行清除,再由GC回收。
    如何正确的释放资源:对于非托管的资源,一般就是,Stream(流),数据库的连接,网络连接等的这些操作系统资源,需要我们手动去释放。
    Net提供了三种释放方法:Dispose,Close,析构函数(也就是Finalize方法)
    第一种:提供Close方法:
    介绍:关闭对象资源,在显示调用时被调用。
    Close 表示什么意思,它会不会释放资源,完全由类设计者决定。因为Close方法是不存在的。你不写就没有。那为什么要加一个Close方法呢?为了避免不熟悉C#语法的开发人员更直观的释放资源,因此提供了Close方法。提供一个Close方法仅仅是为了更符合其他语言(如C++)的规范。正常情况Close方法里面会调用Dispose()方法。
    第二种:Dispose
    继承IDisposable接口,实现Dispose方法;
    介绍:调用Dispose方法,销毁对象,需要显示调用或者通过using语句,在显示调用或者离开using程序块时被调用。
    Dispose方法用于清理对象封装的非托管资源,而不是释放对象的内存,对象的内存依然由垃圾回收器控制。
    Dispose方法调用,不但释放该类的非托管资源,还释放了引用的类的非托管资源。
    Dispose模式就是一种强制资源清理所要遵守的约定;Dispose模式实现IDisposable接口,从而使得该类型提供一个公有的Dispose方法。
    疑问1:Dispose内部到底如何去清理资源的?
    第三种:析构函数(也就是Finalize方法)
    一个正常情况的类是不会写析构函数的,而一旦一个类写了析构函数,就意味着GC会在不确定的时间调用该类的析构函数,判断该类的资源是否需要释放,然后调用finalize方法,如果重写了finalize方法则调用重写的finalize方法。
    Finalize方法的作用是保证.NET对象能在垃圾回收时清除非托管资源。
    在.NET中,Object.Finalize()方法是无法重载的,编译器是根据类的析构函数来自动生成Object.Finalize()方法的
    finalize由垃圾回收器调用;dispose由对象调用。
    finalize无需担心因为没有调用finalize而使非托管资源得不到释放,因为GC会在不确定时间调用,当然,你也可以手动调用finalize方法,而dispose必须手动调用。
    finalize虽然无需担心因为没有调用finalize而使非托管资源得不到释放,但因为由垃圾回收器管理,不能保证立即释放非托管资源;而dispose一调用便释放非托管资源。
    只有类类型才能重写finalize,而结构不能;类和结构都能实现IDispose.原因请看Finalize()特性。
    虽然可以手动释放非托管资源,我们仍然要在析构函数中释放非托管资源,这样才是安全的应用程序。否则如果因为程序员的疏忽忘记了手动释放非托管资源, 那么就会带来灾难性的后果。激荡三十年读后感心得体会,所以说在析构函数中释放非托管资源,是一种补救的措施,至少对于大多数类来说是如此。
    由于析构函数的调用将导致GC对对象回收的效率降低,所以如果已经完成了析构函数该干的事情(例如释放非托管资源),就应当使用SuppressFinalize方法告诉GC不需要再执行某个对象的析构函数。
    析构函数中只能释放非托管资源而不能对任何托管的对象/资源进行操作。因为你无法预测析构函数的运行时机,所以,当析构函数被执行的时候,也许你进行操作的托管资源已经被释放了。这样将导致严重的后果。
    在结构上重写Finalize是不合法的,因为结构是值类型,不在堆上,Finalize是垃圾回收器调用来清理托管堆的,而结构不在堆上。
    带有析构函数的类,生命周期会变长。内存空间需要两次垃圾回收才会被释放,导致性能下降。
    一个带有析构的类,它引用了很多其他的类,这将导致这些类都升到第1代。(gc有0,1,2三代回收机制)。
    5)释放内存:托管堆上的内存由GC全权负责, 值引用的在栈上的内存会随着栈空间的消亡而自动消失。
    GC.Collect(); //强制对所有代进行即时垃圾回收
    当应用程序代码中某个确定的点上使用的内存量大量减少时,在这种情况下使用 GC.Collect 方法可能比较合适。
    疑问2:垃圾回收机制的原理?强制垃圾回收是怎么一回事?
    3,释放模式:是一种微软建议的写法,先手动显示去释放资源,如果忘记了,再让finalize释放资源。如果dispose调用了,则析构不会再调用(使用SuppressFinalize方法取消析构函数的调用)。
    其他:
    如果引用类型对象不再需要,是否需要显式=null;答案是:即使不这样做,GC也会进行垃圾回收。

    百姓地盘、草根平台!(点击修改为自己的签名)客服QQ:139725796
    • 打卡等级:即来则安
    • 打卡总天数:28
    • 打卡月天数:0
    • 打卡总奖励:1687
    • 最近打卡:2026-05-27 22:10:38
         
    年轻就是资本 发表于 2018-8-7 10:59 | 显示全部楼层 来自 中国–广东–汕尾 电信
    百姓地盘、草根平台!(点击修改为自己的签名)客服QQ:139725796
    回复

    使用道具 举报

    • 打卡等级:热心大叔
    • 打卡总天数:173
    • 打卡月天数:16
    • 打卡总奖励:7516
    • 最近打卡:2026-06-17 16:16:43
         
    冰凌儿 发表于 2018-8-7 16:43 | 显示全部楼层 来自 中国–广东–佛山–南海区 电信
    百姓地盘、草根平台!(点击修改为自己的签名)客服QQ:139725796
    回复

    使用道具 举报

    • 打卡等级:热心大叔
    • 打卡总天数:172
    • 打卡月天数:16
    • 打卡总奖励:7100
    • 最近打卡:2026-06-17 16:18:17
         
    心里无交过 发表于 2018-8-7 16:43 | 显示全部楼层 来自 中国–湖北–宜昌 电信
    百姓地盘、草根平台!(点击修改为自己的签名)客服QQ:139725796
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    

    Archiver|手机版|小黑屋|认证|简介|联系我们|赤脚网[临沮网] ( 鄂ICP备18015422号|42052502000021 )

    GMT+8, 2026-6-18 01:39

    Powered by Discuz! X3.5

    © 2001-2026 Discuz! Team.

    网站内容仅代表网友个人观点,非本站认同之观点!删帖请用删帖卡,或在申请版块发帖申请,或联系【 bbs@yalj.net 】我们会尽快处理。
    声明:严禁任何人以任何形式在本站发表与中华人民共和国法律相抵触的言论!
    技术管理:远安坤哥    主办单位:远安县临沮网信息管理中心
                       
    快速回复 返回顶部 返回列表