Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't protected C++-Cli destructors cause compilation errors?

If I compile and run the following:

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;
}

...the code compiles without an error and runs giving me this:

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

If I do the same in C++ I get an error along the following lines:

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'

...so why is it valid in CLI?

like image 949
Jon Cage Avatar asked Apr 13 '15 10:04

Jon Cage


People also ask

What happens if destructor is private in C++?

Private Destructor in C++ This code has private destructor, but it will not generate any error because no object is created.

Can destructor be protected?

If the base class destructor is private or protected then you cannot call delete through the base-class pointer. Use a protected destructor to prevent the destruction of a derived object via a base-class pointer. It limits access to the destuctor to derived classes.

Why is there no exception in destructor?

Most people say never throw an exception out of a destructor - doing so results in undefined behavior. Stroustrup makes the point that "the vector destructor explicitly invokes the destructor for every element. This implies that if an element destructor throws, the vector destruction fails...

Why do destructors Cannot be overloaded?

Destructor has the same name as their class name preceded by a tilde (~) symbol. It is not possible to define more than one destructor. The destructor is only one way to destroy the object create by constructor. Hence destructor can-not be overloaded.


1 Answers

This is a leaky abstraction problem. C++/CLI has several of them, we already went through the const keyword problem. Much the same here, the runtime does not have any notion of a destructor, only the finalizer is real. So it has to be faked. It was pretty important to create that illusion, the RAII pattern in native C++ is holy.

It is faked by bolting the notion of a destructor on top of the IDisposable interface. The one that makes deterministic destruction work in .NET. Very common, the using keyword in the C# language invokes it for example. No such keyword in C++/CLI, you use the delete operator. Just like you would in native C++. And the compiler helps, automatically emitting the destructor calls when you use stack semantics. Just like a native C++ compiler does. Rescuing RAII.

Decent abstraction, but yes, it leaks. Problem is that an interface method is always public. It is technically possible to make it private with explicit interface implementation although it is but a stopgap:

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

Produces a very impressive error list when you try this, the compiler fights back as hard as it can :). C2605 is the only relevant one: "'Dispose': this method is reserved within a managed class". It can't maintain the illusion when you do this.

Long story short, the IDisposable::Dispose() method implementation is always public, regardless of the accessibility of the destructor. The delete operator invokes it. No workaround for this.

like image 163
Hans Passant Avatar answered Oct 31 '22 16:10

Hans Passant