C++ Constructor/Destructor inheritance

92,725

Solution 1

Terminology, terminology...

OK, what do we mean by "Foo is inherited"? We mean that if objects of class A have Foo in its interface, then objects of class B which is a subclass of A also have Foo in its interface.

  • Constructors aren't a part of objects' interface. They belong directly to classes. Classes A and B may provide completely different sets of constructors. No "being inherited" here.

    (Implementation detail: each B's constructors calls some A's constructor.)

  • Destructors indeed are a part of each object's interface, since the object's user is responsible for calling them (i.e. directly with delete or indirectly by letting an object out of scope). Each object has exactly one destructor: its own destructor, which might optionally be a virtual one. It is always its own, and it's not inherited.

    (Implementation detail: B's destructor calls A's destructor.)

So: there's a connection between base and derived constructors and destructors, but it's not like "they're inherited".

I hope this answers what you have in mind.

Solution 2

Q1: What I also know from practice, is that you cannot initialize a derived object with the same prototype than it's parent constructor without explicitly defining a constructor for the derived class, is that correct?

Other than the trivial case where you've defined a default constructor in the superclass, yes you are correct.


Q2: Can anybody who thinks it's not inherited please explain that?

This may be a matter of definitions of terminology. Whilst it's clear that virtual destructors exist and work "as expected", we see in the C++ standard ([class.virtual]):

Even though destructors are not inherited, a destructor in a derived class overrides a base class destructor declared virtual

(emphasis mine)


Q3: So what happens when you call the constructor of a subclass with inputs? Is the "empty constructor" of the superclass called as well?

If you don't explicitly invoke a specific superclass constructor, then the default superclass constructor will be called (assuming it's visible).

Solution 3

Destructors are not inherited. If a class doesn't define one, the compiler generates one. For trivial cases that destructor just calls the base class' destructor, and often that means that there is no explicit code for its destructor (which imitates inheritance). But if a class has members with destructors, the generated destructor calls destructors for those members before calling the base class' destructor. That's something that an inherited function would not do.

Solution 4

Technically, destructors ARE inherited. But in normal circumstances, the inherited destructors are not directly used for a derived class; they're invoked because the derived class's own destructor calls them in order to destroy its own "base class subobjects" as a step within destroying the larger object. And in the unusual circumstances where you do directly use a base class destructor on a derived object, it's very difficult to avoid Undefined Behavior.

This example comes straight from the C++ Standard (12.4p12).

struct B {
  virtual ~B() { }
};
struct D : B {
  ~D() { }
};

D D_object;
typedef B B_alias;
B* B_ptr = &D_object;

void f() {
  D_object.B::~B();              // calls B's destructor
  B_ptr->~B();                   // calls D's destructor
  B_ptr->~B_alias();             // calls D's destructor
  B_ptr->B_alias::~B();          // calls B's destructor
  B_ptr->B_alias::~B_alias();    // calls B's destructor
}

If ~B were not an inherited member of D, the first statement in f would be ill-formed. As it is, it's legal C++, though extremely dangerous.

Solution 5

Inheritance is what : mechanism of reusing and extending existing classes without modifying them, thus producing hierarchical relationships between them.

Inheritance is almost like embedding an object into a class.

when class is inheriting a base class then the base class's constructor is called first then derived class's ,and the destructor's call is in reverse order.

So Why Base Class Constructor is called (called not inherited may be with parameters/default) : to guarantees that the base class is properly constructed when the constructor for the derived class is executed.

Now Calling of Destructor (calling not inherit) : when base object get out of scope then the destructor is called on its own.so there is np issue of inheritance of destructor.

now your questions:

ans 1 - yes you are correct for first question.
ans 2 - so destructor is called not inherited after the scope of object goes out.
& ans 3 - if in derived class you are giving the call with parameters then only that constructor would get called , with it no other constructor would get called.
there is no point of issuse that 2 constructor of same object would get called on object creation,as constructor called at the creation of an object. It prepares the new object for use.so there is no logic of preparing the object twice with different constructors.

Share:
92,725
Jonathan H
Author by

Jonathan H

Computational neuroscientist working in the Bay Area. Previously in academic research modelling brain activity with large systems of delay-differential equations, and developing Bayesian methods for parameter inference. I think programming is an art that complements mathematics. I am proficient in Matlab, very knowledgeable in C++, reasonably good in Python, and occasionally enjoy a bit of JavaScript. I consider LaTeX and bash necessary evils, and fantasize about one day leaving Matlab for Julia.

Updated on November 23, 2020

Comments

  • Jonathan H
    Jonathan H over 3 years

    EDIT : Summary of answers

    In the following, B is a subclass of A.

    It's a matter of terminology; ctors and dtors are not inherited, in the sense that the ctor/dtor of B will not be borrowed from A's interface. A class has at least one constructor, and has exactly one destructor.

    • Constructors:
      • B does not inherit constructors from A;
      • Unless B's ctor explicitely calls one of A's ctor, the default ctor from A will be called automatically before B's ctor body (the idea being that A needs to be initialized before B gets created).
    • Destructors:
      • B does not inherit A's dtor;
      • After it exits, B's destructor will automatically call A's destructor.

    Acknowledgements: I would like to thank especially Oli Charlesworth and Kos for their answers, I set Kos' answer as the solution because it was the one I understood best.


    ORIGINAL POST

    When you search for "C++ destructor inheritance site:stackoverflow.com" on Google, you currently find the following posts:

    1. Constructor and Destructor Inheritance: two users with 30k+ reputation say that it is inherited, and that it's not
    2. Are virtual destructors inherited?: here nothing is mentioned that would point to destructors not being inherited
    3. Destructors and inheritance in C++?: The comments seem to indicate the destructors are inherited

    Q1: What I also know from practice, is that you cannot initialize a derived object with the same prototype than it's parent constructor without explicitely defining a constructor for the derived class, is that correct?


    Even though it's rather clear from the posts that destructors seem to be inherited, I'm still puzzled by the fact that a user with 32k reputation would say its not. I wrote a little example that should clarify everyone's mind:

    #include <cstdio>
    
    /******************************/
    
    // Base class
    struct A
    {
        A() { printf( "\tInstance counter = %d (ctor)\n", ++instance_counter ); }
        ~A() { printf( "\tInstance counter = %d (dtor)\n", --instance_counter ); }
    
        static int instance_counter;
    };
    
    // Inherited class with default ctor/dtor
    class B : public A {};
    
    // Inherited class with defined ctor/dtor
    struct C : public A
    {
        C() { printf("\tC says hi!\n"); }
        ~C() { printf("\tC says bye!\n"); }
    };
    
    /******************************/
    
    // Initialize counter
    int A::instance_counter = 0;
    
    /******************************/
    
    // A few tests
    int main()
    {
        printf("Create A\n"); A a;
        printf("Delete A\n"); a.~A();
    
        printf("Create B\n"); B b;
        printf("Delete B\n"); b.~B();
    
        printf("Create new B stored as A*\n"); A *a_ptr = new B();
        printf("Delete previous pointer\n"); delete a_ptr;
    
        printf("Create C\n"); C c;
        printf("Delete C\n"); c.~C();
    
    }
    

    and here is the output (compiled with g++ 4.4.3):

    Create A
        Instance counter = 1 (ctor)
    Delete A
        Instance counter = 0 (dtor)
    Create B
        Instance counter = 1 (ctor)
    Delete B
        Instance counter = 0 (dtor)
    Create new B stored as A*
        Instance counter = 1 (ctor)
    Delete previous pointer
        Instance counter = 0 (dtor)
    Create C
        Instance counter = 1 (ctor)
        C says hi!
    Delete C
        C says bye!
        Instance counter = 0 (dtor)  // We exit main() now
        C says bye! 
        Instance counter = -1 (dtor)
        Instance counter = -2 (dtor)
        Instance counter = -3 (dtor)
    

    Q2: Can anybody who thinks it's not inherited please explain that?

    Q3: So what happens when you call the constructor of a subclass with inputs? Is the "empty constructor" of the superclass called as well?

  • Jonathan H
    Jonathan H over 11 years
    Thank you :) So for Q2, what does it mean exactly? From what I understand of the quote; destructors are not inherited, which means deleting B should not decrement the counter.
  • Oliver Charlesworth
    Oliver Charlesworth over 11 years
    @Sh3ljohn: What, specifically, are you asking whether "it's not supposed to be like that"?
  • Jonathan H
    Jonathan H over 11 years
    Sorry I was editing my comment when you posted your answer, this should be clearer.
  • Oliver Charlesworth
    Oliver Charlesworth over 11 years
    @Sh3ljohn: The superclass destructor are implicitly called by the subclass destructor, whether it's explicitly defined or not.
  • Bart van Ingen Schenau
    Bart van Ingen Schenau over 11 years
    @Sh3ljohn: The term 'inherited' has a very specific meaning within the C++ standard that differs slightly from the common usage of the term. This is the main area where that difference manifests itself. What the C++ standard is essentially stating is that the destructor of a base class is not part of a derived class (it is not inherited in that sense). But the virtual-call mechanism is still required to work if destructors are involved (so in that sense, destructors are 'inherited')
  • Jonathan H
    Jonathan H over 11 years
    So it's all about the word "inheritance" right? Because C's destructor still calls A's destructor in the example.
  • Pete Becker
    Pete Becker over 11 years
    if C::f() calls A::f(), it is not the case that C inherits A::f.
  • Jonathan H
    Jonathan H over 11 years
    Comment 1: I just don't understand your first sentence, wouldn't it be "then objects of class B which is a subclass of A also have Foo in their interface"?
  • Jonathan H
    Jonathan H over 11 years
    Comment 2: So, as with the two comments of Oli Charlesworth and Pete Becker, it seems to be a matter of terminology. It's not inherited, but it calls automatically something from the superclass, is that it?
  • Kos
    Kos over 11 years
    @Sh3ljohn Comment 1: Of course you're right, I'll edit and fix. Comment 2: Yes, "not inherited" as in "not in the public interface of derived objects", unlike other public class members (methods and fields).
  • aschepler
    aschepler over 11 years
    Hmm. So why does an example in the Standard use, and g++ allows, d.B::~B();?
  • aschepler
    aschepler over 11 years
    Hmm. So why does an example in the Standard use, and g++ allows, d.B::~B();?
  • Jonathan H
    Jonathan H over 11 years
    What if you made ~B() non-virtual? Would any of the above examples fail?
  • aschepler
    aschepler over 11 years
    I think they would all be well-formed, but all do the same thing (call B's destructor, not D's).
  • Pete Becker
    Pete Becker over 11 years
    @aschepler - inheritance is about interfaces: if you don't write it in the derived class but it's available from the base, it's inherited.
  • Jonathan H
    Jonathan H over 10 years
    I disagree, IMO the destructor of a class should be implemented such that it handles potentially multiple calls (typically, test a member pointer before deleting it). In my case I don't really care if the integer gets negative, and I don't care about the prints either (see comment "we exit main now"). You should delete your post because it doesn't provide an answer to the OP (which already has one btw), if you leave it up people are probably going to downvote...
  • BigPeteB
    BigPeteB about 10 years
    Unless you know your particular C++ implmentation supports it, calling destructors multiple times isn't safe. "Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended" (C++ standard §12.4.14)
  • Jonathan H
    Jonathan H about 10 years
    Actually you are right :) I still think it's usually a small effort to make sure the behavior is defined in that case.. You should put this in comment though, this really has nothing to do with the question :)
  • Abhishek Mane
    Abhishek Mane about 3 years
    you did not mention for constructors in ans2. can you please tell about it also ?