为什么不保护C ++ - Cli析构函数会导致编译错误?

时间:2015-04-13 10:38:43

标签: c++-cli destructor

如果我编译并运行以下内容:

using namespace System;

ref class C1
{
public:
    C1()
    {
        Console::WriteLine(L"Creating C1");
    }

protected:
    ~C1()
    {
        Console::WriteLine(L"Destroying C1");
    }
};

int main(array<System::String ^> ^args)
{

    C1^ c1 = gcnew C1();
    delete c1;

    return 0;
}

...代码编译没有错误,并运行给我这个:

Creating C1
Destroying C1
Press any key to continue . . .

如果我在C ++中做同样的事情,我会在以下几行中收到错误:

1>ProtectedDestructor.cpp(45): error C2248: 'C1::~C1' : cannot access protected member declared in class 'C1'
1>          ProtectedDestructor.cpp(35) : compiler has generated 'C1::~C1' here
1>          ProtectedDestructor.cpp(23) : see declaration of 'C1'

...为什么它在CLI中有效?

2 个答案:

答案 0 :(得分:14)

这是漏洞抽象问题。 C ++ / CLI有几个,我们已经经历了 const 关键字问题。在这里大致相同,运行时没有任何析构函数的概念,只有终结器是真实的。所以它必须是假的。创造这种错觉非常重要,原生C ++中的RAII模式是神圣的。

通过将析构函数的概念固定在IDisposable接口的顶部来伪造它。使确定性破坏在.NET中起作用的那个。非常常见的是,C#语言中的 using 关键字会调用它。在C ++ / CLI中没有这样的关键字,您使用delete运算符。就像在原生C ++中一样。编译器可以帮助您在使用堆栈语义时自动发出析构函数调用。就像本机C ++编译器一样。拯救RAII。

体面的抽象,但是,它泄漏了。问题是接口方法总是公开的。技术上可以通过显式接口实现将其设置为私有,尽管它只是一个权宜之计:

public ref class Foo : IDisposable {
protected:
    //~Foo() {}
    virtual void Dispose() = IDisposable::Dispose {}
};

当你尝试这个时,产生一个非常令人印象深刻的错误列表,编译器尽可能地反击:)。 C2605是唯一相关的一个:&#34;&#39; Dispose&#39;:此方法在托管类中保留&#34;。当你这样做时,它不能保持幻觉。

简而言之,IDisposable :: Dispose()方法实现始终是公共的,无论析构函数的可访问性如何。 delete运算符调用它。没有解决方法。

答案 1 :(得分:5)

除了Hans的详细答案外,C {/ CLI对象上的delete实际上是IDisposable接口的激活,接口继承总是公共 1 ,它可能是富有成效地问

  

如何调用受保护的析构函数呢?

编译器生成的Dispose方法调用用户定义的析构函数。由于此Dispose方法是该类的成员,因此它可以访问protectedprivate类成员,例如析构函数。

(在本机C ++中,编译器不受可访问性规则的约束,因为它是强制执行它们的。在.NET中,IL验证器强制执行它们。)


1 实际上,他的解释主要是编译器不允许显式实现IDisposable::Dispose(),在这种情况下它可能是私有成员。但这完全无关紧要。可以通过声明类型联系virtual个成员。并且delete不会调用object->Dispose(),而是调用safe_cast<IDisposable^>(object)->Dispose()