Thread: About inhertance (virtual destructors)

  1. #1
    Registered User
    Join Date
    Sep 2018
    Posts
    179

    About inhertance (virtual destructors)

    In the following snippet:
    Code:
    #include <iostream>
    using namespace std;
    
    class Drawable {
    public:
        virtual ~Drawable() { cout << "Destuctor of main class called\n"; }
    };
    
    class MyDrawable : public Drawable {
    public:
        ~MyDrawable() {
            cout << "Destructor called\n";
        }
    };
    
    void deleteDrawable(Drawable *drawable) {
        delete drawable;
    }
    
    int main() {
        deleteDrawable(new MyDrawable);
        cin.get();
    }
    Why does ~Drawable() need to be virtual in order for the destructor of the MyDrawable to execute?

    Also when ~Drawable() is virtual, still both the destructors are called.
    Code:
    Destructor called
    Destuctor of main class called
    Also, the pointer is of type Drawable, so it holds no information about what the type it's pointing to.

    So how does the compiler know which destructor to call? There could be several child classes for Drawable..

  2. #2
    Registered User john.c's Avatar
    Join Date
    Dec 2017
    Posts
    554
    The idea of virtual methods is that they can run the child's methods from a pointer to the parent.
    Code:
    #include <iostream>
    #include <vector>
    using namespace std;
      
    class Drawable {
    public:
        virtual void draw() const = 0;
        virtual ~Drawable() { cout << "Drawable dtor\n"; }
    };
      
    class DrawableA : public Drawable {
    public:
        void draw() const { cout << "DrawableA draw\n"; }
        ~DrawableA() { cout << "DrawableA dtor\n"; }
    };
      
    class DrawableB : public Drawable {
    public:
        void draw() const { cout << "DrawableB draw\n"; }
        ~DrawableB() { cout << "DrawableB dtor\n"; }
    };
      
    int main() {
        vector<Drawable*> v {
            new DrawableA,
            new DrawableB,
            new DrawableB,
            new DrawableA
        };
        for (const Drawable* d: v)
            d->draw();
        for (Drawable* d: v)
            delete d;
    }
    Code:
    $ g++ -std=c++11 drawable.cpp
    $ ./a.out
    DrawableA draw
    DrawableB draw
    DrawableB draw
    DrawableA draw
    DrawableA dtor
    Drawable dtor
    DrawableB dtor
    Drawable dtor
    DrawableB dtor
    Drawable dtor
    DrawableA dtor
    Drawable dtor
    Last edited by john.c; 1 Week Ago at 12:34 PM.
    Let him who is not come to logic be plagued with continuous and everlasting filth.
    - John of Salisbury, 1160

  3. #3
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    26,319
    Quote Originally Posted by Nwb
    Also when ~Drawable() is virtual, still both the destructors are called.
    The derived class destructor has to invoke the base class destructor to destroy the base class subobject otherwise you end up with a partially destroyed object.

    Quote Originally Posted by Nwb
    Also, the pointer is of type Drawable, so it holds no information about what the type it's pointing to.

    So how does the compiler know which destructor to call? There could be several child classes for Drawable..
    That's implementation defined, but this article seems to do a good job of giving an overview of the typical implementation: the virtual table
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  4. #4
    Registered User
    Join Date
    Sep 2018
    Posts
    179
    Thanks a lot john.c and laserlight.

    So let me clarify.

    When we have an object of a child class, it has nothing to do with the parent class (just that it inherits everything). Its constructor consists of the parent's constructor followed by its own constructor. And its destructor consists of its own destructor and then the parent's destructor.

    When we have a pointer of the type of the parent class and we point to the child class, really the pointer thinks it is pointing to the parent class (so we can't use the methods of the child class) and by the use of virtual table, we are able to execute the virtual method in the child class.
    And when we call the destructor, really we are calling the child class's constructor which is nothing but its own destructor followed by the paren't destructor.

    Please tell me if that is right. And if it is then I can't thank you enough john.c and laserlight, that really helped a lot!

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    26,319
    Yes, that sounds like one way to describe it, except for a typo ("constructor" should have been "destructor").
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    Registered User
    Join Date
    Sep 2018
    Posts
    179
    But... But.. what's the other way to describe it?
    What is actually happening tho?

    Btw I couldn't connect to learncpp.com for some reason so I didn't read your link but I googled about vtables.

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    26,319
    Quote Originally Posted by Nwb
    what's the other way to describe it?
    There are many correct ways to describe, some of which might be more accurately phrased than yours, some of which might be less accurately phrased. For example:

    When we have an object of a child class, it has nothing to do with the parent class (just that it inherits everything). Its constructor invokes the parent class constructor before proceeding with construction specific to the child class. And its destructor proceeds with destruction specific to the child class and then invokes the parent class destructor.

    When we have a pointer of the type of the parent class and we point to an object of the child class, we will call member functions of the parent class and cannot call member functions present in the child class but not present in the parent class through that pointer without casting. However, through the mechanisms of polymorphism, typically implemented as a vtable, we are able to execute virtual member functions that the child class overrides.

    And when we call the virtual destructor, really we are calling the child class' destructor which invokes the parent class destructor after performing destruction specific to the child class.

    Quote Originally Posted by Nwb
    Btw I couldn't connect to learncpp.com for some reason so I didn't read your link but I googled about vtables.
    Yeah, it looks like they are having some database issue, but it was one of the higher ranked search results when I last checked.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Registered User
    Join Date
    Sep 2018
    Posts
    179
    Okay gotcha so the constructor and destructor of the child class don't have the parent's constructor and destructor themselves, it's just that they call the constructor and destructor of the parent class.

    But see, you can only call the member functions of a class only when an object of the class is instantiated. So does that mean every single child class instantiates an instance of the parent class? Or how exactly does this work?

    So having to instantiate a parent class and having to call functions in the parent class would be way more costly than just copying the constructor and destructor's code of the parent's class to child's class right.. So why this choice?

    And even if there isn't instantiating of an instance of the parent class, we still have to 'call' functions lying outside. Function pointers take a byte right, so we could have avoided storing 2 bytes by simply copying code from the parent class.

    I assume that function pointers take a byte because that's what sizeof() said, I donno.

    Thanks for helpin' laserlight

  9. #9
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    26,319
    Quote Originally Posted by Nwb
    But see, you can only call the member functions of a class only when an object of the class is instantiated. So does that mean every single child class instantiates an instance of the parent class? Or how exactly does this work?
    Yes, that is called the parent class subobject (or "base class subobject", or just "base subobject").

    Quote Originally Posted by Nwb
    So having to instantiate a parent class and having to call functions in the parent class would be way more costly than just copying the constructor and destructor's code of the parent's class to child's class right.. So why this choice?
    That's like saying that calling a function would be way more costly than always inlining the code of the function that is called. Wrong. It depends on the situation, and if you write the parent class constructor such that it is a candidate for inlining, the compiler could choose to inline it if according to the compiler's metrics it is a good fit for inlining.

    Quote Originally Posted by Nwb
    And even if there isn't instantiating of an instance of the parent class, we still have to 'call' functions lying outside. Function pointers take a byte right, so we could have avoided storing 2 bytes by simply copying code from the parent class.
    No, compilers typically do not use function pointers to invoke non-virtual member functions of parent classes called via objects of child classes since it is possible to determine at compile time what member function should be invoked.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  10. #10
    Registered User
    Join Date
    Sep 2018
    Posts
    179
    Okay, so the parent class subobject consists of all non-virtual members. And the child class doesn't copy the non-virtual members from the parent class but rather when we make a call to a non-virtual method in a child class, it relays the call to the parent class. Is that how this works?

    And what in the case of virtual members? Would the parent class subobject not have virtual members at all (because they're never used right, as they're defined in the child class)? And the child class would have the virtual members defined so it doesn't need to relay calls to the virtual members.

    Also the parent class subobject and the child class must have the same scope right (because of non-virtual data members)?
    So is the class subobject ughm, part of the child object?
    Last edited by Nwb; 1 Week Ago at 04:26 AM.

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    26,319
    Quote Originally Posted by Nwb
    Okay, so the parent class subobject consists of all non-virtual members.
    No, the parent class subobject is, at least conceptually, an object of the parent class that is contained in an object of a child of that base class.

    Furthermore, objects of class type don't consist of member functions. Rather, member functions are part of the class that such objects are instantiated from. C++ doesn't allow for monkey patching of member functions such that different objects of the same class can have different member functions, so both conceptually and by implementation, these member functions don't belong to any particular object.

    (Well, you could simulate such monkey patching by having member variables that are function pointers, but that's a different scenario... and after all each instance of a class has its own set of member variables.)

    Quote Originally Posted by Nwb
    And the child class doesn't copy the non-virtual members from the parent class but rather when we make a call to a non-virtual method in a child class, it relays the call to the parent class. Is that how this works?
    Yes. Perhaps consider this simplified example:
    Code:
    class A
    {
        // ...
        void foo();
        // ...
    };
    Now, I define A::foo in a separate source file, and compile it into a library file that I provide to you along with the header file containing the definition of class A. You derive class B from A:
    Code:
    class B : public A
    {
        // ...
    };
    // ...
    B b;
    b.foo();
    When you compile your code, your compiler doesn't have access to the definition of A::foo. But when it sees b.foo(), it can determine that this foo is actually A::foo, and hence arrange for A::foo to be invoked. This doesn't require a function pointer, no more than calling a non-member function whose definition is not available requires a function pointer.

    Quote Originally Posted by Nwb
    And what in the case of virtual members? Would the parent class subobject not have virtual members at all (because they're never used right, as they're defined in the child class)? And the child class would have the virtual members defined so it doesn't need to relay calls to the virtual members.
    Each polymorphic class has its own vtable, and so the function pointers in the child class vtable would be replaced for those virtual function overrides. It is true though that if you don't actually use polymorphism, i.e., the compiler can determine at compile time which virtual member function to call, then chances are the compiler won't arrange to make the function call via the vtable, but rather treat it as if it were a non-virtual member function.

    Quote Originally Posted by Nwb
    Also the parent class subobject and the child class must have the same scope right (because of non-virtual data members)?
    The notion of scope has to do with portions of program text in which a name refers to a particular entity. So, it doesn't make sense to say "the parent class subobject and the child class must have the same scope".

    Quote Originally Posted by Nwb
    So is the class subobject ughm, part of the child object?
    The parent class subobject is part of the child class object. That's what "subobject" means.
    Last edited by laserlight; 1 Week Ago at 04:42 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    Registered User
    Join Date
    Sep 2018
    Posts
    179
    Ah I was confusing what I had read about static functions and thinking that every object has its own instance of functions but I get that the difference between static and non-static functions is just that one gets *this pointer and the other doesn't.

    Thanks for clarifying about subobjects, so essentially they're separate objects that are linked to specific child objects. This also makes sense as regards to why child classes aren't able to access private data in the parent class.
    Can you suggest me a good place to read about how polymorphism is implemented? It seems to be an interesting topic.

    Thanks for helpin' laserlight

  13. #13
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    26,319
    Quote Originally Posted by Nwb
    so essentially they're separate objects that are linked to specific child objects. This also makes sense as regards to why child classes aren't able to access private data in the parent class.
    No, that's just access control enforced by the compiler. If not, it would be impossible for them to access public and protected data too.

    Quote Originally Posted by Nwb
    Can you suggest me a good place to read about how polymorphism is implemented? It seems to be an interesting topic.
    Read up further on vtable implementations.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  14. #14
    Registered User
    Join Date
    Sep 2018
    Posts
    179
    ah okie

    Inheriting privately, protectedly and publicly would mean that the corresponding parent class subobject would have either all private functions, protected and private functions or all public, private and protected functions. Correct?

  15. #15
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    26,319
    Yes.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. virtual inhertance *intersting*
    By Alexpo in forum C++ Programming
    Replies: 2
    Last Post: 09-16-2009, 06:15 PM
  2. virtual destructors
    By coletek in forum C++ Programming
    Replies: 2
    Last Post: 01-11-2009, 12:14 PM
  3. virtual destructors
    By crvenkapa in forum C++ Programming
    Replies: 7
    Last Post: 06-01-2007, 04:57 PM
  4. Replies: 2
    Last Post: 06-11-2006, 05:56 PM
  5. Virtual & Pure virtual destructors
    By BMJ in forum C++ Programming
    Replies: 61
    Last Post: 08-22-2002, 09:38 AM

Tags for this Thread