(c++) subclass chain destruction when calling destructor - c++

class A {
....
};
class B: public A {
int x;
...
};
class C: public A{
A * ptr;
.....
};
class D : public A {
A* ptr1;
A* ptr2;
....
};
Note: I made all the constructors for B,C,D just didn't include them in there.
So A (with no fields) is the super class and I have 3 subclasses (B,C and D) each with different fields.
A is an abstract class and its mostly chaining of Class (B,C,D)
So like I might have a situation like
B *x = new B {5};
B *x2 = new B {5}
D * y = new D{x,x2);
So when I do delete y; I want to make it chain destruct the 2 pointers of its two fields which are (B objects). How would I make the destructor for class D then chain destruct?
Like the example I show is really simple but other examples have more and more layers. I want to make sure that everything is deleted so no memory leaks occur.
Should my dtor for Class D look like this ?
~D(){
delete ptr1;
delete ptr2;
}
and for the case of class C would I just do this?
~C(){
delete ptr;
}
Because I did this and it doesnt work I get memory leaks so whats wrong?

I assume your question is mistaken since you say it's a chain of inheritance but at time of writing B, C and D are inhereting directly from A.
When you do new D what happens is you have a composite object consisting of A, B, C and D - they are constructed in inheritance order.
*y = {
// B starts
int x;
// C starts
A * ptr;
// D starts
A * ptr1;
A * ptr2;
};
Note that destructors will be called in reverse order, D first then C and so forth, to clean up the memory in reverse order (to deal with dependencies on superclass). However this depends on what sort of object you are deleting.
If we did this:
D * y = new D;
A * x = y;
delete x;
we have effectively treated y as an A object, meaning we are limited to what an A object knows about and can do so long as we're using x. If we delete using x, we are going to call ~A() rather than ~D(). This is why in this situation the destructors need to be virtual, which allows virtual ~A() to call ~D(), which will eventually call ~A() again (the real one not the virtual despatch).
Regardless of whether you are or are not using polymorphism / upcasting, your intuition is correct - ~D() should only clean up memory that D objects introduced to the class (ptr1 and ptr2), likewise for ~C() (only ptr).
The leak would be because of object slicing happening somewhere. For example (if you forced this to compile), this is worse than the above example because the object data won't be copied (as oppose to the structure definition being incomplete relative to the sort of object being referred to):
D d;
C * c = d; // not too bad, we can't refer to D-specific things.
C c2 = d; // bad, c2 is copied from D up to C superclass and no further.
Be very careful that you only pass the pointers / references to objects that may need to hold derived types, and where you do make sure you use virtual destructors. Otherwise what you're doing is correct, letting each class's destructor clean up just for that class.

Related

Address of upcast object

Suppose B is a base class of D (maybe virtual, maybe multiple inheritance, need not be a direct base class).
Let obj be an object of type D (not of a subclass of D -- exactly D).
Let
D * d = std::addressof(obj);
B * b = d;
Can we safely assume that
(char*) d <= (char*) b && (char*) b < (char*) d + sizeof(D)
?
Background: This is to become a step in a routine determining whether some object has been created by placement new in a particular aligned_storage. I need to be sure that, if yes, all pointers to base objects of this object point to some address within the aligned_storage.
I am pretty sure that your assumption is safe given D is the final type of the object. Otherwise it would be treacherous to use placement new in the first place.
#include <stdlib.h>
#include <new>
struct B { int i; };
struct D : virtual B { int j; };
int
main()
{
auto const storage = malloc(sizeof(D));
D* d = new (storage) D();
free(storage);
return 0;
}
If B were located before d then the placement new would need to return a pointer adjusted based on the layout of D but "the standard allocation function void* operator new(std::size_t, void*) ... simply returns its second argument unchanged." (http://en.cppreference.com/w/cpp/language/new) Likewise, the storage of B cannot be situated such that it extends beyond (char*)d + sizeof(D) because it would overrun the memory allocated.
Thanks for sharing an interesting question. Perhaps since asking the question you have already found a more satisfactory answer. I would be interested in reading a more concrete proof why the assumption holds or does not.

Does deleting the main class's object pointer delete nested classes pointers as well?

I have a class inside a class, nested class
class A {
public:
int a;
int b;
class B {
int c;
int d;
}
}
In the process, I allocate the class B's object pointer in heap.
B *bobj = new B();
A *a;
auto_ptr<A> A1(new A());
a = A1.release();
Does deleting the a, delete bobj as well??
delete a;
or should we explicitly delete the sub class pointer as well?
This is in a code not written by me, I had put the logs at all alloc and deallocs and I see that B *bobj = new B(), for this there is no delete and for A's object ptr there is a delete. And yet there is no memory leak.That is why I got a doubt what happens in this scenario.
I think you're slightly confused here. If you have an object of type A, it doesn't have a B object inside of it. It just has two ints, a and b. Your class B is just declared as a nested class inside A - similar to declaring it in a namespace. It must be referred to as A::B from outside the class (and must be public to do so).
In the sample code you gave, B *bobj = new B(); A *a;, you're not even creating an A object. I presume you meant this (assuming you make B public):
A::B *bobj = new A::B();
A *a = new A();
Both a and bobj are completely separate objects. They have nothing to do with each other. You have to delete both of them.
If instead you had done this:
class B {
int c;
int d;
};
class A {
int a;
B b;
};
Now an object of class type A has a member called b which is of type B. That b member is part of any object of type A. So if you do A* a = new A();, you get an A object with a B object inside it. You must only do delete a;.
The golden rule: only delete what you have newed.
The way you have written the code, class B is a nested type within class A, but no instance of class B is contained in class A, meaning that the instantiation--and therefore destruction--must be managed separately. So yes, you need to delete both if you new both.
It depends on what your destructor doing. If A contains pointer of type B and in destructor of A this pointer is released - yes. If A doesn't contains pointer of type B and/or in A's destructor pointer to B is not released - you should release it manually.
In short, C++ doesn't automatically delete object pointers (without specialize auto-pointers etc.). So, explicitly delete them in your program.
delete a; call destructor of A class. You can write code to delete B's object inside destructor.

Why there are no memleaks in the following C++ while assign?

the following code demotes my belief that I know C++ more or less. Why valgrind does not show any memleak here? Why I expect memleaks:
B is larger than A: it contains an additional member; so on assign there should be class' fields slicing.
~A() does not have a virtual dtor. So when we call delete a only ~A() should be called and memory allocated in B would lost.
But I receive that the calling order of dtors is: ~A(), ~B(), ~A(). Why?!
struct A {
~A()
{
std::cerr << "~A" << std::endl;
}
};
struct B : A {
int* data;
B() : data(new int[20]) {}
~B()
{
std::cerr << "~B" << std::endl;
delete [] data;
}
};
main():
A* a = new A;
B* b = new B;
*a = *b;
delete a;
delete b;
UPD:
Shame on me! I muddled deletion of an object by the pointer of base class when virtual dtor should called. Here just contents of class copying. Thanks everyone!
delete a;
a is A*, so A::~A is called
delete b;
b is B* so B::~B is called.
Why would there be a problem?
Class fields' slicing? Yeah, so? You're just copying the A fields from *b to *a, nothing more than that. No memory is lost here.
In *a = *b you are copying (and yes, slicing) *b to *a. However, that leaves *b unchanged; it doesn't get sliced. When you delete b, you are asking for ~B() to be called on *b.
The *a = *b; assignment only copy the common A part of the data (and A has no fields).
You made a delete for each new , so there is no memleak.
*a =*b
does not allocate anything, the copy constructor just copies the part of b which can go to a, but it does not call new..
By invoking *a = *b;, you're implicitly calling the assignment operator function created by the compiler that looks like this:
A &operator=( const A &other ){
}
which in your case doesn't do anything since A has no member variable.
Your class A is empty, so it is entirely irrelevant for the purpose of this discussion. The only code that is involved in dynamic allocation is:
B* b = new B;
delete b;
Since you delete an object created with new, there is no leak. Furthermore, the class B contains sufficient code to clean up its own internal allocation in this straight-forward use-case.
(Note that class B is still horribly broken, since it will not survive copying or assignment. The classic Rule of Three (or Five).)
Because what you do here is object slicing. I think you intended to have the base pointer point to the derived object and since the base destructor is not virtual, derived class destructor not called during cleanup. But that is not the case here.
*a = *b
The compiler will 'slice' those derived part of the object and copy only the base part of the object. This is not usually one want to do because this will result in an incomplete object with only base class properties and specialized derived class properties missing.

Overriding function in C++ doesn't work

#include <cstdio>
using namespace std;
class A {
public:
virtual void func() { printf("A::func()"); }
};
class B : public A {
public:
virtual void func() { printf("B::func()"); }
};
int main() {
A a = *(A *)new B();
a.func();
}
The question is simple: why a->func() calls function in class A even though a contains object of class B?
A a = *(A *)new B();
a.func();
Here's what happens in this code, step by step:
new B(): a new object of type B is allocated on the free store, resulting in its address
(A*): the address of the object is cast to A*, so we have a pointer of type A* actually pointing to an object of type B, which is valid. All OK.
A a: here the problems start. A new local object of type A is created on the stack and constructed using the copy constructor A::A(const A&), with the first paremeter being the object created before.
The pointer to the original object of type B is lost after this statement, resulting in a memory leak, since it was allocated on the free store with new.
a.func() - the method is called on the (local) object of class A.
If you change the code to:
A& a = *( A*) new B();
a.func();
then only one object will be constructed, its pointer will be converted to pointer of type A*, then dereferenced and a new reference will be initialized with this address. The call of the virtual function will then be dynamically resolved to B::func().
But remember, that you'd still need to free the object since it was allocated with new:
delete &a;
Which, by the way, will only be correct if A has a virtual destructor, which is required that B::~B() (which luckily is empty here, but it doesn't need to in the general case) will also be called. If A doesn't have a virtual destructor, then you'd need to free it by:
delete (B*)&a;
If you would want to use a pointer, then that's the same as with the reference. Code:
A* a = new B(); // actually you don't need an explicit cast here.
a->func();
delete (B*)a; // or just delete a; if A has a virtual destructor.
Now that you've modified your code snippet, the problem is clear. Polymorphism (i.e. virtual functions) are only invoked via pointers and references. You have neither of these. A a = XXX does not contain an object of type B, it contains an object of type A. You've "sliced away" the B-ness of the object by doing that pointer cast and dereference.
If you do A *a = new B();, then you will get the expected behaviour.
The problem you encounter is classic object slicing :
A a = *(A *)new B();
Make a either a reference or pointer to A, and virtual dispatch will work as you expect. See this other question for more explanations.
You commented on another answer that "Compiler should at least give warning or what". This is why is it considered a good practice to make base classes either abstract of non copyable : your initial code wouldn't have compiled in the first place.
This might do that trick.
A &a = *(A *)new B();
a.func();
Or
A *a = new B();
a->func();
Virtual dispatch works only with pointer or reference types:
#include <cstdio>
using namespace std;
class A {
public:
virtual void func() { printf("A::func()"); }
};
class B : public A {
public:
virtual void func() { printf("B::func()"); }
};
int main() {
A* a = new B();
a->func();
}
The problem is the deference and casting of B to A with the A a = *(A *)new B();
You can fix it with just removing the *(A *) changing it to (A *a = new B(); ) but I would take it a step further since your variable name is not good for instantiation of B.
It should be
B *b = new B();
b->func();
Because you performed slicing when you copied the dynamically allocated object into object a of type A (which also gave you a memory leak).
a should be a reference (A&) instead, or just keep the pointer.

how to assign a Base object to a Derived object

I have a question about C++, how to assign a Base object to a Derived object? or how to assign a pointer to a Base object to a pointer to a Derived object?
In the code below, the two lines are wrong. How to correct that?
#include <iostream>
using namespace std;
class A{
public:
int a;
};
class B:public A{
public:
int b;
};
int main(){
A a;
B b;
b = a; //what happend?
cout << b.b << endl;
B* b2;
b2 = &a; // what happened?
cout << b->b << endl;
}
It makes no sense to assign a base object to a derived (or a base pointer to a derived pointer), so C++ will do its best to stop you doing it. The exception is when the base pointer really points at a derived, in which case you can use dynamic cast:
base * p = new derived;
derived * d = dynamic_cast <derived *>( p );
In this case, if p actually pointed at a base, the pointer d would contain NULL.
When an object is on the stack, you can only really assign objects of the same type to one another. They can be converted through overloaded cast operators or overloaded assignment operators, but you're specifying a conversion at that point. The compiler can't do such conversions itself.
A a;
B b;
b = a;
In this case, you're trying to assigning an A to a B, but A isn't a B, so it doesn't work.
A a;
B b;
a = b;
This does work, after a fashion, but it probably won't be what you expect. You just sliced your B. B is an A, so the assignment can take place, but because it's on the stack, it's just going to assign the parts of b which are part of A to a. So, what you get is an A. It's not a B in spite of the fact that you assigned from a B.
If you really want to be assigning objects of one type to another, they need to be pointers.
A* pa = NULL;
B* pb = new B;
pa = pb;
This works. pa now points to pb, so it's still a B. If you have virtual functions on A and B overrides them, then when you call them on pa, they'll call the B version (non-virtual ones will still call the A version).
A* pa = new A;
B* pb = pa;
This doesn't work. pa doesn't point B, so you can't assign it to pb which must point to a B. Just because a B is an A doesn't mean than an A is a B.
A a;
B* pb = &a;
This doesn't work for the same reason as the previous one. It just so happens that the A is on the stack this time instead of the heap.
A* pa;
B b;
pa = &b;
This does work. b is a B which is an A, so A can point to it. Virtual functions will call the B versions and non-virtual ones will call the A versions.
So, basically, A* can point to B's because B is an A. B* can't point to A because it isn't a B.
The compiler won't allow that kind of thing. And even if you manage to do it through some casting hack, doing so makes no sense. Assigning a derived object to a pointer of a base makes sense because everything that base can do, derived can do. However, if the opposite case was allowed, what if you try to access a member defined in derived on a base object? You would be trying to access an area of memory filled with garbage or irrelevant data.
b = a; //what happend?
This is plain illegal - A is not B, so you can't do it.
b2 = &a; // what happened?
Same here.
In neither case, the compiler wouldn't know what to assign to int b, hence he prevents you from doing that. The other way around (assigning Derived to Base) works, because Base is a subset of Derived.
Now if you would tell us, what exactly you want to achieve, we might help you.
If it's a case of assigning an A that is known to be a Derived type, you can do a cast:
A* a = new B();
B* b = dynamic_cast<B>(a);
Just remember that if a is not a B then dynamic_cast will return NULL. Note that this method works only on pointers for a reason.
Derived object is a kind of Base object, not the other way around.