Even more fun with virtual methods


OK, after the last post I expect many people will nail this, so why don’t you show it your friends that don’t read my blog and see if they pass 😉 

Create a new instance of Derived, drop the reference, wait for the GC to collect it, and for any finalizers to run.  What is the output? 

And of course, the important question: why?

 

public class Base

{

    public virtual void DoCleanUp() {

        Console.WriteLine(“Do Base’s Cleanup”);

    }

    ~Base()

    {

        DoCleanUp();

    }

}

public class Derived: Base

{

    public override void DoCleanUp()

    {

        Console.WriteLine(“Do Derived Cleanup”);

    }

    ~Derived()

    {

        DoCleanUp();

    }

}

 

Comments (46)

  1. The result is "Do Derived Cleanup" prints twice on the screen.

    Reson:

    Destructors are called in the opssite way to the contructors are called. So ~Derive is called and it prints one "Do Derived Cleanup". Since it is inherited from Base then the ~Base is called. But the Call do DoCleanup from Base causes to call the DoCleanup method in the derive class since the object instance is Derive. Consequently it prints "Do Derived Cleanup" again!

  2. My guess: it will print "Do Derived Cleanup" twice.

    The reason I believe is that a Finalize method (~Derived) automatically calls the base class’ Finalize (~Base) before returning, but the object maintains its type (Derived) all along so that both calls to DoCleanUp end up in Derived.DoCleanUp.

    So this really is the same thing as the previous quiz, with the addition of the automatic base.Finalize() call.

    I haven’t tested any of this though.

    And I have no friends to show it to, they’re all a bunch of Linux freaks :).

  3. Stuart says:

    I think the answer is this:

    Do Derived Cleanup

    Do Derived Cleanup

    Figuring out why is left as an exercise for another reader. Assuming I’m right, of course.

  4. The output is:

    Do Derived Cleanup

    Do Derived Cleanup

    This is because the Finalizer on Derived is called first, invoking its DoCleanUp method, then the Finalizer on Base is called, invoking DoCleanUp, which by being virtual, activates the derived class’s overriding method.

  5. Dan McKinley says:

    Piece of cake – "Do Derived Cleanup" is printed twice, because all of the finalizers are called.

  6. Ken says:

    Derived’s DoCleanUp will be called twice, once by Derived’s "destructor", and once by Base’s.

  7. Derived.DoCleanup will be called twice. First from inside ~Derived(), second from inside ~Base().

    ~Derived() body is executed first, which calls Derived’s DoCleanUp(). After that all base class destructors are called. The DoCleanUp() call in ~Base() calls Derived’s DoCleanup() because, it’s a Derived object which is currently destructing and that has overridden Base.DoCleanUp()

  8. Sean Malloy says:

    Destructors are implicitly chained.

    so Derived.DoCleanUp gets called twice, because the call in Base object calls the Derived override.

    Looking at the docos: "destructors in an inheritance chain are called in order, from most derived to least derived."

  9. First, the Derived destructor is invoked which will print "Do Derived Cleanup". Then, the base destructor will be invoked which will also print "Do Derived Cleanup" because the method is virtual.

  10. The output is:

    Do Derived Cleanup

    Do Derived Cleanup

    When Derived’s Finalize method calls Base’s Finalize method (after it completes it’s own cleanup) Base’s Finalize method makes a virtual call to DoClean, which is dispatched to overridden implementation of class Derived.

    So Base’s cleanup is not done and resource leakage might occur.

    HG

  11. Alex says:

    OK, let me try.

    Output should be twice:

    "Do Derived Cleanup"

    "Do Derived Cleanup"

    This is due the finalizer of the Derived class will call the finalizer of the Base and Base will therefore call the overridden DoCleanUp() on the Derived again, since we have an instance of Derived and not Base.

    Base calls this.DoCleanUp();. Then Base calls this.Finalize(); In fact we have two finalizations. Is this necessary? Do we have to supress the second finalize?

    Alex

  12. David Levine says:

    My first impulse is that you will see…

    Do Derived Cleanup

    Do Derived Cleanup

    I ran this as a test and sure enough that’s what it printed out.

    The reason is that objects are created from the inside-out and finalized from the outside-in, and unless eplicitly overridden, invoking a virtual method will call into the virtual method in a derived class, not from the base class. So the order of execution is…

    1. Invoke ~Derived(), which invokes

    2. Derived.DoCleanUp()

    3. invoke ~Base(), which invokes

    4. Derived.DoCleanUp()

  13. I would guess you would get:

    Do Derived Cleanup

    Do Derived Cleanup

    because it will call both destructors and each will called Derived’s override of DoCleanUp. Sadly I’ve never even used descructors in C# so I could be way off on this one.

  14. The output will be:

    "Do Derived Cleanup"

    "Do Base’s Cleanup"

    The first destructor to get called will be that of Derived. Then, by the time Base’s destructor gets called the Derived object no longer exists, nor does it’s vtable, therefore Base::DoCleanUp is called instead.

  15. Mark Mullin says:

    That’s easy

    what

    Do Derived Cleanup

    Do Derived Cleanup

    why

    When the derived class finalizer runs it calls the overloaded DoCleanUp. Then the base class finalizer gets invoked – invoking the base class finalizer on an instance of a derived class means the base class is operating on an instance of the derived class – so it’s DoCleanUp ends up invoking the derived classes DoCleanUp again

    so I have a question

    Is there any way to have a base class indicate it wants to call it’s own version of a virtual and not one of the overrides when the instance is a derivative ?

  16. Tobin Titus says:

    The console will display the derived cleanup’s code twice.

  17. I’m fairly sure that _both_ lines are going to be printed to the console.

    What i’m not sure of is the order – my feeling is that the object is destroyed, and then the base object is destroyed, so it would print in that order. But i don’t know. 🙂

  18. sebmol says:

    Impossible to tell. There is no guarantee that finalizers are ever executed. If they were, you should see "Do Derived Cleanup" twice for the same reasons you’d see it twice if it was invoked in the constructor.

  19. J.Marsch says:

    Ok, my first time trying to really analyze IL, so go easy on me:

    In the IL for Derived.Finalize, we perform a callvirt instance void Base::DoCleanup() Since it’s a callvirt, we get the derived version of DoCleanup().

    Next, we perform a call instance void Base::Finalize()

    That is going to invoke Base’s finalizer.

    Base.Finalize also peforms a callvirt to Base::DoCleanup(). Since it’s a callvirt, we get the overridden implementation of DoCleanup().

    Is that close?

  20. Philip Rieck says:

    Since no-one else has commented, I will. If I’m wrong (since I don’t have a compiler near me) Then this is the *other* Philip Rieck.

    This would print out:

    Do Derived Cleanup

    Do Derived Cleanup

    Why? Well, two reasons, really — Firstly, you really, really should call your base classes Finalize method in your Finalize. Since this is such a strong rule, the C# compiler forces you to. That is, at the end of your Finalize method a call to Base.Finalize() is added at compile time.

    I’m not sure, but I do think that if you try to add this call yourself you get a compile error.

    Second reason, In Base.Finalizer you will have a "callvirt instance void Base::DoCleanup()" — and since this object is a "Derived" object, it will call the most derived DoCleanUp accessible — Derived.DoCleanUp().

    This is so similar to the dispose pattern that a code reviewer should really say "Make this the Dispose convention used in .net, ensure it gets called once (Disposed = true), and call Base.Dispose from derived (in a finally block).

  21. Merak says:

    The derived DoCleanUp gets called twice.

    First when Derived is finalized, then again when Base is finalized (due to the DoCleanUp call being overriden)

    It would probably have been better written if the base DoCleanUp, and the finalizer both called a common (private) cleanup function.

    (or relying on Derived to call base.DoCleanUp <shudder>)

  22. Merak says:

    The derived DoCleanUp gets called twice.

    First when Derived is finalized, then again when Base is finalized (due to the DoCleanUp call being overriden)

    It would probably have been better written if the base DoCleanUp, and the finalizer both called a common (private) cleanup function.

    (or relying on Derived to call base.DoCleanUp <shudder>)

  23. YM says:

    IMHO the following might illustrate the point a bit better

    public class Base

    {

    public virtual void DoCleanUp() {

    Console.WriteLine("Do Base’s Cleanup");

    }

    ~Base()

    {

    Console.WriteLine("~Base() started");

    DoCleanUp();

    Console.WriteLine("~Base() completed");

    }

    }

    public class Derived: Base

    {

    public override void DoCleanUp()

    {

    Console.WriteLine("Do Derived Cleanup");

    }

    ~Derived()

    {

    Console.WriteLine("~Derived() started");

    DoCleanUp();

    Console.WriteLine("~Derived() completed");

    }

    }

  24. YM says:

    IMHO the following might illustrate the point a bit better

    public class Base

    {

    public virtual void DoCleanUp() {

    Console.WriteLine("Do Base’s Cleanup");

    }

    ~Base()

    {

    Console.WriteLine("~Base() started");

    DoCleanUp();

    Console.WriteLine("~Base() completed");

    }

    }

    public class Derived: Base

    {

    public override void DoCleanUp()

    {

    Console.WriteLine("Do Derived Cleanup");

    }

    ~Derived()

    {

    Console.WriteLine("~Derived() started");

    DoCleanUp();

    Console.WriteLine("~Derived() completed");

    }

    }

  25. Merak says:

    Actually (adding to my previous reply) I’m really surprised that Console is even available during finalization !

  26. It will output "Do Derived Cleanup" twice.

    Once when the derived class’s destructor executes, and again when the automatically-generated call to the base class’s destructor executes. In both cases, the object is still an instance of Derived, so normal virtual method resolution applies.

  27. Max says:

    It prints

    "Do Derived Cleanup" two times.

    The reason: The Dtor of Derived calls the virtual method DoCleanUp wich prints "Do Derived Cleanup" the first time. Next the Dtor of Base gets automatically called after the Derived Dtor is finished. The Dtor of Base again calls DoCleanUp and since DoCleanUp ist virtual Derived.DoCleanUp gets called a second time.

  28. Josh Pollard says:

    It will call the Derived class’ DoCleanUp method twice. I don’t know the exact reason. I do know that it is because it calls it once to kill the derived class object, and once again when it attempts to kill the base object. It does this because of it being a virtual method that is overridden.

  29. RJ says:

    I’m going to be so embarrised if I get this wrong, but….

    I think it will print "Do Derived Cleanup" twice.

    Reason: destructors will be called outer to inner. But the inner constructor will callvirt the outer’s DoCleanUp method.

    Oh lord, please say it ain’t so.

  30. Dilip says:

    "Do Derived Cleanup" is printed 2 times. Probably because by the time the finalizer for the base portion is executed the object is still not fully collected which preserves the virtual dispatch mechanism. So the DoCleanup call in ~Base() still translates to a callvirt instruction thereby calling Derived.DoCleanUp() — that is if you obviously have created a Derived object in main().

  31. Stuart says:

    Moderated comments suck…

    Looking forward to hopefully seeing the comments posted to this one before the article falls off the bottom of blogs.msdn.com…

  32. Stuart says:

    Hey, that actually went through? Okay, well in that case, I’ll post my answer (which I also posted last night, but it went into the moderation void)…

    I believe the result will be:

    Do Derived Cleanup

    Do Derived Cleanup

    I haven’t tested this but I’m pretty confident in the hypothesis. I’ll leave it to others to explain why – assuming I’m right, of course.

  33. Diego Mijelshon says:

    As Stuart mentioned, it’s:

    Do Derived Cleanup

    Do Derived Cleanup

    The first one corresponds to Derived and the second to Base, since destructors are called in reverse order.

    This could be a big problem if the classes where dealing with real resources.

  34. Random coder says:

    Is the solution to avoid calling virtual methods in destructors, having a policy that says methods like DoCleanUp() must call base.DoCleanUp() as the last line, or something else? This is a little off the top of my head, so these suggestions might be simplistic.

  35. Mark H says:

    The output will be:

    Do Derived Cleanup

    Do Base’s Cleanup

    The Derived object’s destructor will get called and all reference to the Derived instance will be removed. What is left is a Base object. The Base object’s destructor will then get called, but since the Derived aspect of the object no longer exists, the Base::DoCleanUp is call instead.

  36. Robert Kozak says:

    Looks like some people are confused on this issue.

    I agree with Mark H. As soon as I saw it I knew that the output would be:

    Do Derived Cleanup

    Do Base’s Cleanup

    And I think this makes perfect sense.

  37. Darren Bennett says:

    Output:

    Do Derived Cleanup

    Do Derived Cleanup

    The first corresponds to the ~Base destructor (since base objects are GC’d first). Since the destructor is calling a virtual method it calls the version of DoCleanup from the Derived class

    The second corresponds to the ~Derived destructor (since it is the last object in the v-table set for destruction). It calls the same DoCleanup, thus getting duplicate output.

  38. Darren Bennett says:

    Output:

    Do Derived Cleanup

    Do Derived Cleanup

    The first corresponds to the ~Base destructor (since base objects are GC’d first). Since the destructor is calling a virtual method it calls the version of DoCleanup from the Derived class

    The second corresponds to the ~Derived destructor (since it is the last object in the v-table set for destruction). It calls the same DoCleanup, thus getting duplicate output.

  39. JD says:

    Robert Kozak, Mike H: You’re expecting the C++ behavior.

    The CLR is avoiding that fixup work done by C++.

  40. Robert Kozak says:

    Did you try it JD?

    🙂

  41. I don’t know if JD did, but *I* did. JD is right, and you are wrong Robert. Did *you* try it? 😉

  42. Mark H says:

    Ah, you guys cheated and actually compiled and ran the code! Since when are quizes open book? 😉

  43. Robert Kozak says:

    One of my co workers compiled it after I showed it to him. I initially thought it would be

    Do Derived Cleanup

    Do Derived Cleanup

    But when he ran the code it was

    Do Derived Cleanup

    Do Base’s Cleanup

    Now I have to go back and see what is going on. Now I am thoroughly confused 🙂

  44. Robert Kozak says:

    Damn. Ok double checked the code. My buddy cut and pasted and forgot to change the virtual to an override. I feel like an idiot. But no worries thats a normal feeling for me. 🙂

Skip to main content