测试类:

#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("终结器:!");
}