views:

134

answers:

7
#include<iostream>
using namespace std;

class A
{
public:
        int i;
        A() {cout<<"A()"<<endl;}
        ~A() {cout<<"~A()"<<endl;}
};
class B:public A
{
public:
        int j;
        B(): j(10)
        {
                this->i=20;
                this->~A();
        }
};

int main()
{
        B abc;
        cout<<"i="<<abc.i<<" j="<<abc.j<<endl;
}//main

Two questions:

  1. How come A's destructor gets called like an ordinary function instead of destroying the object? (or is it some kind of rule that the base class will be destroyed only if the child class's destructor calls the base class's destructor?) I was trying out this sample code to find out how the destructor works. So if simply calling the destructor function does not destruct the object, then there is obviously some other kind of call that calls the destructor and only then the object is destructed. What's so special in that kind of call and what call is it?
  2. Is there a way to have an initialization list for A in B's constructor? Something like this:

    class B:public A
    { 
        B(): j(10), A():i(20) {}
    };
    
+5  A: 
  1. The base class's destructor should be virtual. Here, as it's created on the stack, it's not problem, but anyway..
  2. No, but you can call the class A() constructor in the initialize list of B's constructor, like this:
    B(): A( .. ), ...

A* a = new B();
//..
delete a;

will not call B's destructor unless class A destructor is virtual. That's why STL containers should not be derived - theirs destructors are not virtual.

Kiril Kirov
Nope. Having the base class's constructor as virtual does not destruct A.
Nav
There's no need to have a virtual destructor unless an object of a child class is destroyed through a pointer to the base class.
Victor Nicollet
Alf P. Steinbach
**@Nav** - noone talks about constructors, but destructors. And don't call `this->~A();` in B's constructor ... **@Victor** - yep, you're right about pointers. Here there's no such problem, as the object is not created using new, but still.. **@Alf** - Yes, it's not requered, but it should be, right? Or, there could be memory leaks in some situations. And I agree with the Undefined Behavior (:
Kiril Kirov
I was just testing it out to find out how these things work. For me, knowing the concepts from the ground level is very important. Innovative testing/experimenting with basic concepts teaches me a lot.
Nav
What I really wanted to know was how the destructor function works. ie: will just calling the destructor destruct the object or not. Apparently here, calling the destructor isn't enough. Something else happens under the hood. I want to know what that is.
Nav
-1 for replacing original error (corrected) with "STL containers should not be derived". That's an urban legend. One seldom if ever allocate such containers dynamically, it serves no purpose. Cheers,
Alf P. Steinbach
I didn't get that `replacing original error (corrected) with "STL containers should not be derived"`. Anyway, OK for the STL containers and creating the dynamically, I agree with you, but still ..
Kiril Kirov
+4  A: 
  1. Destructor is like any other normal function which you can call (but you should never do it unless you use a placement new). When you call delete on a object two things happen: Destructor is called for cleanup and then operator delete is called to release the memory allocated for the object. Here the second step is not happening.

  2. No, you can not call it like that. What you can do is some thing like this:

    class A { public: A(int n) : i(n){} };

    class B : public A { public: B() : A(20), j(10){} };

Naveen
A situation where it's acceptable to call the destructor is when you're managing the underlying object memory yourself, and created the object using placement new. Of course, it's exceedingly rare.
Victor Nicollet
Agreed, updated accordingly.
Naveen
+1  A: 

1) Destructor calling order in C++ is reverse order of the constructor calling order. So first derived class object get destroy and then base class object.

2) No.

user001
+2  A: 

For point:

  1. This is an undefined behaviour but only ~A() is called though an instance of class B because ~A() is not declared virtual. See more on Wikipedia.
  2. No. For derived classes, first call your parent class, then assign parameters.

For point 1) on Wikipedia:

having no virtual destructor, while deleting an instance of class B will correctly call destructors for both B and A if the object is deleted as an instance of B, an instance of B deleted via a pointer to its base class A will produce undefined behaviour.

Example (for point 2):

B(): A(), j(10) {}

or

B(): A() {j = 10;}
The Elite Gentleman
+1  A: 

In the code that you are giving, you are indeed destroying the base class and as such i. Calling a destructor and then using the dead object is undefined behavior - it may work or it may crash.

Should i was something that is more complex that an int (for example a vector), trying to do anything with that would probably result in a crash.

ipapadop
+1  A: 

If you call ~SomeClass() yourself, explicitly, the destructor function will be called. Which leaves the object (in this case, the base class part of the object) in a destroyed state.

Since the destructor is not virtual, the destructor of derived classes will not be called, but base classes of SomeClass will be destroyed too.

Trying to find out if A is really destroyed by just using the i member, is not a good test. In fact, you can't test this, since using the object results in undefined behavour. It may work, or it may not (in your case, it probably will print "i=20 j=10", but i is already destroyed).

Jan
+2  A: 

@Nav: no, your understanding of "destroyed" is just wrong. When an object's destructor is called, the object is destroyed. You seem to believe that the memory it resided in evaporates entirely, but that never happens. The object no longer exists, but some garbage data is typically left over by the object, and if you're willing to break the rules of C++ and invoke undefined behavior, then you can read those leftover bytes, and they'll look like the object, and because there are no runtime checks on whether you're accessing a valid object, you can often treat them as an object. Which you do.

It's illegal, it's undefined behavior, but in practice it often works.

Once again, a destructor does not physically vaporize the memory. Your RAM still has the same capacity after a destructor has executed. Conceptually, the object no longer exists once the destructor has run. But the data it contained is still there in memory.

jalf
+1 for understanding the OP's problem with the whole "destruction" concept.
Pedro d'Aquino