C ++ constructor / destructor inheritance

EDIT: Summary of Answers

Subsequently, B is a subclass of A.

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

  • Constructors :
    • B does not inherit constructors from A;
    • If B ctor explicitly calls one of A ctor, by default ctor from A will be called automatically before the body of B ctor (the idea is that A needs to be initialized before B is created).
  • destructors :
    • B does not inherit A dtor;
    • After it exits, destructor B will automatically call the destructor.

Acknowledgment: I would especially like to thank Oli Charlworth and Kos for their answers, and I put the Kos solution as the solution because it was the one I understood best.




ORIGINAL MAIL

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

  • Inheritance of constructor and destructor : two users with a 30k + reputation say that they are inherited and that it is not
  • Are virtual destructors inherited? : nothing is mentioned here, which indicates that destructors are not inherited
  • Destructors and inheritance in C ++? : comments show that destructors are inherited

Q1: What I also know from practice is that you cannot initialize a derived object using the same prototype as its parent constructor, without explicitly defining the constructor for the derived class, is this correct?




Despite the fact that from the messages that seem to be inherited, it’s clear enough that I'm still puzzled by the fact that a user with a 32x reputation will say that this is not the case. I wrote a small example that should clarify all thoughts:

#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 result (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: Could someone who believes that this has not been inherited explain that?

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

+49
c ++ inheritance constructor destructor
Jan 6 '13 at 16:46
source share
6 answers

Terminology, terminology ...

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

  • Constructors are not part of the object interface. They belong directly to the classes. Classes A and B can provide completely different sets of constructors. There are no "inherited" ones.

    (Implementation details: each constructor of B calls some constructor of A.)

  • Destructors are indeed part of every object interface, since the user of the object is responsible for calling them (i.e., directly using delete or indirectly if the object is out of scope). Each object has exactly one destructor : its own destructor, which can be optionally virtual. He is always his own, and he is not inherited.

    (Implementation details: calls to destructors B Destructor.)

So: there is a connection between basic and derived constructors and destructors, but that doesn't look like they are inherited.

I hope this answers what you mean.

+27
Jan 6 '13 at 17:07
source share

Q1: What I also know from practice is that you cannot initialize a derived object with the same prototype as its parent constructor without explicitly defining a constructor for the derived class, is this true?

Aside from the trivial case where you defined a default constructor in a superclass, yes, you're right.




Q2: Could someone who believes that this has not been inherited explain that?

It may be a matter of defining terminology. Despite the fact that all virtual destructors exist and work "as expected", we see in the C ++ standard ([class.virtual]):

Although destructors are not inherited , the destructor in the derived class overrides the base class destructor declared virtual

(my accent)




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

If you do not explicitly call the specific superclass constructor, then the default superclass constructor will be called (assuming it is visible).

+7
Jan 06 '13 at 16:53
source share

Destructors are not inherited. If the class does not define one, the compiler generates . For trivial cases, the destructor simply calls the base class destructor, and often this means that there is no explicit code for its destructor (which mimics inheritance). But if the class has members with destructors, the generated destructor calls the destructors for these members before calling the base class destructor. This is what the function did not inherit.

+4
Jan 06 '13 at 17:11
source share

Inheritance is that: a mechanism for reusing and expanding existing classes without modifying them, thereby creating hierarchical relationships between them.

Inheritance is almost like embedding an object in a class.

when a class inherits a base class, then the constructor of the base class is called the first, then the derived class, and the call to the destructor is in reverse order.

So, why is the Base Class Constructor called (called non-inherited may be with the / default options): ensures that the base class will be properly constructed when the constructor is executed for the derived class.

Now the call to the destructor (the call is not inherited): when the base object goes out of scope, then the destructor is called on its own. There is also an np destructor inheritance problem.

now your questions:

ans 1 - yes, you are right for the first question.
ans 2 - therefore, the destructor is called not inherited after the scope of the object is missing.
& Amp; ans 3 - if in a derived class you call a call with parameters, then only that constructor will be called, in which no other constructor will be called.
there is no point in assuming that the constructor of the same object will be called when the object is created, since the constructor called when the object is created. He is preparing a new object for use. Since there is no logic for preparing an object twice with different constructors.

+3
Jan 6 '13 at 17:18
source share

Technically, destructors are inherited. But under normal conditions, inherited destructors are not directly used for the derived class; they are called because the native destructor of the derived class calls them to destroy their own "base class subobjects" as a step in destroying a larger object. And in unusual circumstances, when you directly use the base class destructor on the derived object, it is very difficult to avoid Undefined Behavior.

This example is provided directly 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 destructor B_ptr->~B(); // calls D destructor B_ptr->~B_alias(); // calls D destructor B_ptr->B_alias::~B(); // calls B destructor B_ptr->B_alias::~B_alias(); // calls B destructor } 

If ~B were not an inherited member of D , the first operator in f would be poorly formed. Be that as it may, it is legal C ++, albeit extremely dangerous.

+3
Jan 06 '13 at
source share

In your example, you explicitly call the destructor functions. This is legal (obviously because it is compiled and launched), but almost always incorrect.

For dynamic allocation objects created using new , the destructor will be launched when the object object is deleted using delete .

For statically allocated objects that are created simply by declaring the object within the scope of the function, the destructor is started when the object's area disappears. That is, when main() completes, object destructors will be launched. But you already run destructors for these objects, calling them manually! That's why in your example, the output shows that the counter decreases to -3 ... you ran the destructors for a , b and c twice.

Here is the same code annotated to display when the destructors start automatically:

 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; // Implicitly calls destructor for a_ptr. a_ptr is class B, // so it would call a_ptr->~B() if it existed. Because B is an A, after // its destructor is called, it calls the superclass destructor, // a_ptr->~A(). printf("Create C\n"); C c; printf("Delete C\n"); c.~C(); } // Function exits here at the close brace, so anything declared in its scope is // deallocated from the stack and their destructors run. // First `c` is destroyed, which calls c.~C(), then because C is a subclass of A // calls c.~B() (which doesn't exist, so a blank implementation is used), then // because B is a subclass of A calls c.~A(). This decrements the counter, but // the count is wrong because you already manually called c.~C(), which you // ordinarily shouldn't have done. // Then `b` is destroyed, in a similar manner. Now the count is off by 2, // because you had already called b.~B(). // Lastly `a` is destroyed, just as above. And again, because you had already // called a.~A(), the count is now off by 3. 
+1
Mar 28 '14 at 18:04
source share



All Articles