【问题标题】:Why destruct is being called here explicitly?为什么在这里显式调用 destruct ?
【发布时间】:2017-07-06 06:47:46
【问题描述】:

在浏览一些与通过 SMTP 发送电子邮件相关的代码时,我在 MSDN 中得到了以下 sn-p。

static void CreateMessageWithAttachment( String^ server )
{
   String^ file = L"data.xls";

   MailMessage^ message = gcnew MailMessage( L"jane@contoso.com",L"ben@contoso.com",L"Quarterly data report.",L"See the attached spreadsheet." );

   Attachment^ data = gcnew Attachment(file, MediaTypeNames::Application::Octet);

   ContentDisposition^ disposition = data->ContentDisposition;
   disposition->CreationDate = System::IO::File::GetCreationTime( file );
   disposition->ModificationDate = System::IO::File::GetLastWriteTime( file );
   disposition->ReadDate = System::IO::File::GetLastAccessTime( file );

   message->Attachments->Add( data );

   SmtpClient^ client = gcnew SmtpClient( server );

   client->Credentials = CredentialCache::DefaultNetworkCredentials;
   client->Send( message );

   data->~Attachment();
   client->~SmtpClient();
}

我只是想知道他们为什么在这里调用析构函数?我在这里遗漏了什么吗?

 data->~Attachment();
 client->~SmtpClient();

【问题讨论】:

    标签: c++11 c++-cli destructor


    【解决方案1】:

    在 C++/CLI 中,ref 类析构函数是对 Dispose pattern 的抽象。

    编译后的以下 C++/CLI 类:

    public ref class Test
    {
    public:
        Test() { System::Console::WriteLine("ctor"); }
        ~Test() { System::Console::WriteLine("dtor"); }
    
        static void Foo()
        {
            auto foo = gcnew Test();
            foo->~Test();
        }
    };
    

    反编译为以下 C# 代码(C# 语义更接近底层 IL 代码,因此这是可视化所发生情况的好方法):

    public class Test : IDisposable
    {
        public Test()
        {
            Console.WriteLine("ctor");
        }
    
        private void ~Test()
        {
            Console.WriteLine("dtor");
        }
    
        public static void Foo()
        {
            new Test().Dispose();
        }
    
        protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool A_0)
        {
            if (A_0)
            {
                this.~Test();
            }
            else
            {
                this.Finalize();
            }
        }
    
        public virtual void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize((object)this);
        }
    }
    

    可以看到 dispose 模式是编译器自动实现的。

    ~Test“析构函数”编译为私有方法,并为您生成IDisposable::Dispose的实现。出于某种原因,编译器还会调用(空)终结器。

    此外,正如您在静态 Foo 方法中看到的,foo->~Test(); 被简单地转换为对 Dispose 的调用。编译器不会让你直接调用foo->Dispose();

    但是标准调用“析构函数”的方法(以及 Dispose 方法)是使用 delete 关键字:delete foo; 与 C++ 中的 foo->~Test(); 相同/CLI 当foo 是托管句柄时。

    注意,在这个例子中,不要写:

    auto foo = gcnew CppCli::Test();
    foo->Whatever();
    delete foo;
    

    您可以使用堆栈语义并编写:

    Test foo;
    foo.Whatever();
    

    foo.~Test(); 将在foo 超出范围时被调用,就像在常规 C++ 中一样。


    为了完整起见,以下是整个事物与终结器交互的方式。让我们添加一个:

    public ref class Test
    {
    public:
        Test() { System::Console::WriteLine("ctor"); }
        ~Test() { System::Console::WriteLine("dtor"); }
        !Test() { System::Console::WriteLine("finalizer"); }
    };
    

    这将反编译为以下类似 C# 的代码:

    public class Test : IDisposable
    {
        public Test()
        {
            Console.WriteLine("ctor");
        }
    
        // This is the real finalizer
        ~Test()
        {
            this.Dispose(false);
        }
    
        // This is what C++/CLI compiles ~Test to
        // Let's call this MethodA
        private void ~Test()
        {
            Console.WriteLine("dtor");
        }
    
        // This is what C++/CLI compiles !Test to
        // Let's call this MethodB
        private void !Test()
        {
            Console.WriteLine("finalizer");
        }
    
        [HandleProcessCorruptedStateExceptions]
        protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool A_0)
        {
            if (A_0)
            {
                this.~Test(); // MethodA, NOT the finalizer
            }
            else
            {
                try
                {
                    this.!Test(); // MethodB
                }
                finally
                {
                    base.Finalize();
                }
            }
        }
    
        public virtual void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize((object)this);
        }
    }
    

    请注意,为了增加混淆,在 C# 中,终结器是 ~Test(),这与 C++/CLI 编译器为您的析构函数生成的 private void ~Test() 函数不同

    【讨论】:

    • 非常感谢您的详细解释,真的很有帮助。
    • 您可能还应该注意~Test() 语法不是标准的。处置托管对象的规范方法是使用delete,与使用 C++ 对象相同。
    • @DavidYaw 哎呀,你说得对,这很重要,谢谢!
    • @DavidYaw 是的,那肯定不会那么混乱:)
    猜你喜欢
    • 1970-01-01
    • 2017-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-05
    相关资源
    最近更新 更多