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模式中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了