C++CLI 析构函数和终结器理解

测试类:

#pragma once
ref class Hello
{
public:
    Hello();
   ~Hello();
   !Hello();
};


/***************/


#include "Hello.h"

Hello::Hello()
{
    System::Console::WriteLine("构造函数!");
}

Hello::~Hello()
{
    System::Console::WriteLine("析构函数!");
}

Hello::!Hello()
{
    System::Console::WriteLine("终结器:!");
}

主函数:

#include "Hello.h"

using namespace System;


int main()
{
    {
        Hello ^hello = gcnew Hello();
        delete hello; // 显式释放
    }

    {
        Hello h;  // 这种栈对象,超过作用域会调用析构函数。也是显式释放。
    }

    String^ info = Console::ReadLine();
    Console::WriteLine("信息:{0}", info);
    GC::Collect(); // 垃圾回收
    return 0;
}

上面的主函数,运行输出会这样:

构造函数!
析构函数!
构造函数!
析构函数!

如果,动态对象不显示释放:

int main()
{
    {
        Hello ^hello = gcnew Hello();
        //delete hello; // 不显式释放
    }

    {
        Hello h;  // 这种栈对象,超过作用域会调用析构函数。也是显式释放。
    }

    String^ info = Console::ReadLine();
    Console::WriteLine("信息:{0}", info);
    GC::Collect(); // 垃圾回收
    return 0;
}

输出应该这样:

构造函数!
构造函数!
析构函数!
终结器:!

经测试,Hello ^hello = gcnew Hello(); 这种动态对象,如果用 delete 显式释放,会调用析构函数。终结器是在GC回收前调用的;如果显式释放调用了析构函数,就不会再调用终结器了。所以要注意这个。 gcnew 动态调用,不显式释放,GC 回收的话会调用终结器。所以一般用下面这种方式,保证什么情况都能释放非托管内存:

Hello::~Hello()
{
    // 这里释放托管资源 
    System::Console::WriteLine("析构函数!");
    !Hello();   // 在析构函数里再调用终结器
}

Hello::!Hello()
{
    //  这里释放非托管资源 
    System::Console::WriteLine("终结器:!");
}

C# 中一般使用IDisposable接口:

public class MyClass : IDisposable
{
    private bool _disposed = false;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // 防止终结器再次执行
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // 清理托管资源
            }

            // 清理非托管资源
            _disposed = true;
        }
    }

    // 终结器依然可以存在,作为最后的清理手段
    ~MyClass()
    {
        Dispose(false);
    }
}

通过实现IDisposable接口和提供一个Dispose方法,你可以更主动、确定地管理资源的生命周期,这对于包含非托管资源(如文件句柄、数据库连接等)的对象尤为重要。

托管 C++:

ref class MyClass : public System::IDisposable
{
private:
    bool disposed = false;

public:
    // 实现IDisposable接口
    virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // 清理托管资源
            }

            // 清理非托管资源
            // ...

            disposed = true;
            // 如果是从Dispose()调用的,告诉GC不需要调用终结器
            GC::SuppressFinalize(this);
        }
    }

    // 公共Dispose方法
    void Dispose()
    {
        Dispose(true);
        GC::SuppressFinalize(this);
    }

    // 析构器,间接实现终结器逻辑
    !MyClass()
    {
        Dispose(false);
    }

protected:
    ~MyClass(){} // 这里是托管C++特有的析构函数声明,但实际清理逻辑应在Dispose中完成
};

在上述例子中,Dispose(bool disposing)方法负责实际的资源清理工作,根据disposing参数决定是否清理托管资源。!MyClass()是一个析构器,当对象被垃圾回收时,它会调用Dispose(false)来释放非托管资源。而显式的~MyClass()声明是托管C++类的一部分,但通常仅作为标记,实际的清理逻辑应集中于Dispose模式中。

posted @   double64  阅读(99)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示