C++ Constructor/Destructor inheritance
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
andB
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.
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, 2020Comments
-
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:
- Constructor and Destructor Inheritance: two users with 30k+ reputation say that it is inherited, and that it's not
- Are virtual destructors inherited?: here nothing is mentioned that would point to destructors not being inherited
- 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?
-
Constructors:
-
Jonathan H over 11 yearsThank 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 over 11 years@Sh3ljohn: What, specifically, are you asking whether "it's not supposed to be like that"?
-
Jonathan H over 11 yearsSorry I was editing my comment when you posted your answer, this should be clearer.
-
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 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 over 11 yearsSo it's all about the word "inheritance" right? Because C's destructor still calls A's destructor in the example.
-
Pete Becker over 11 yearsif
C::f()
callsA::f()
, it is not the case thatC
inheritsA::f
. -
Jonathan H over 11 yearsComment 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 over 11 yearsComment 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 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 over 11 yearsHmm. So why does an example in the Standard use, and g++ allows,
d.B::~B();
? -
aschepler over 11 yearsHmm. So why does an example in the Standard use, and g++ allows,
d.B::~B();
? -
Jonathan H over 11 yearsWhat if you made
~B()
non-virtual? Would any of the above examples fail? -
aschepler over 11 yearsI think they would all be well-formed, but all do the same thing (call
B
's destructor, notD
's). -
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 over 10 yearsI 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 about 10 yearsUnless 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 about 10 yearsActually 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 about 3 yearsyou did not mention for constructors in ans2. can you please tell about it also ?