how to assign a Base object to a Derived object - c++

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.

Related

(c++) subclass chain destruction when calling destructor

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.

how to access variables in top layer class from the base class pointer

I have a base class and a derived class
class A
{
public:
int a;
}
class B : public A
{
public:
int b;
}
if i have a pointer of type A to a B object, how can i retrieve the pointer to the B object?
i.e.
A * ptrA = new B;
how can i use ptrA to access b?
If you are sure the A* is actually pointing to a B, you can do it unconditionally with no performance overhead:
A * ptrA = new B;
B * ptrB = static_cast<B *>(ptrA);
ptrB->b = 42;
This will result in undefined behavior if ptrA doesn't actually point to an instance of B. If you aren't sure, you can check it this way:
if (B * ptrB = dynamic_cast<B *>(ptrA)) {
ptrB->b = 42;
} else {
// it's not a B, do something else
}
The difference here is that dynamic_cast will actually check at runtime to make sure the cast is good, and return NULL if not.
Please note that dynamic_cast will only work if your base class has at least one virtual method. Thanks to Dietmar Kühl for pointing that out in the comments.
Since your A* was obtained by a direct implicit conversion from B* you can use
static_cast<B*>(ptrA)->b
If you need to do so frequently in your code rather than just once in a blue moon, there is something wrong with your design though.
You may cast like (B*)ptrA->memberOfB() in c-style cast. Or more safely: dynamic_cast<A*>(ptrA).
ptrB = dynamic_cast<A*>(ptrA);
if(ptrB!=null)
{
//casted properly
ptrB->memberOfB();
}
else
{
//can not be casted
}

Downcasting a class c++

I have a doubt about downcasting an object in C++.
Here it comes an example:
class A { }
class B : public A {
public:
void SetVal(int i) { _v = i; }
private:
int _v;
}
A* a = new A();
B* b = dynamic_cast<B*>(a);
b->SetVal(2);
What would it happen with this example? We are modifying a base clase like if it is a child one... how does it work related with the memory?
With this cast... Is it like creating an instance of B and copying the values of A?
Thanks
A* a;
This just gives you a pointer to an A. It doesn't point anywhere in particular. It doesn't point at an A or B object at all. Whether your code works or not depends on the dynamic type of the object it is pointing at.
So there are two situations you might want to know about. First, this one:
A* a = new A();
B* b = dynamic_cast<B*>(a);
b->SetVal(2);
This will give you undefined behaviour because the dynamic_cast will return a null pointer. It returns a null pointer when the dynamic type of the object is really not a B. In this case, the object is an A. You then attempt to dereference the null pointer with b->SetVal(2), so you get undefined behaviour.
A* a = new B();
B* b = dynamic_cast<B*>(a);
b->SetVal(2);
This will work fine because the object really is a B object. The dynamic cast will succeed and the SetVal call will work just fine.
However, note that for this to work, A must be a polymorphic type. For that to be true, it must have at least one virtual member function.
That shouldn't even compile, because the classes aren't polymorphic so you can't use dynamic_cast.
If it did, it would be undefined behavior.

static_cast and reference to pointers

Can anyone tell me why this doesn't compile:
struct A { };
struct B : public A { };
int main()
{
B b;
A* a = &b;
B* &b1 = static_cast<B*&>(a);
return 0;
}
Now, if you replace the static cast with:
B* b1 = static_cast<B*>(a);
then it does compile.
Edit: It is obvious that the compiler treats A* and B* as independent types, otherwise this would work. The question is more about why is that desirable?
B is derived from A, but B* isn't derived from A*.
A pointer to a B is not a pointer to an A, it can only be
converted to one. But the types remain distinct (and the
conversion can, and often will, change the value of the
pointer). A B*& can only refer to a B*, not to any other
pointer type.
non-constant lvalue reference (B*&) cannot bind to a unrelated type (A*).
Handling of references is something the compiler does for you, there should be no need to cast to reference.
If we refactor the code to:
B b;
A* a = &b;
B* b_ptr = static_cast<B*>(a);
B*& p1 = b_ptr;
It will compile.
You are trying to cast an A* to a B*. This is the wrong way around and not very useful. You probably want to store a pointer to derived in a pointer to base, which is useful and doesn't even need a cast.
I suppose a dynamic_cast might work here, but the result is implementation defined if I'm not mistaken.

C++ Overloading assignment operator: "cannot convert `B*` to `A*`" error

class B;
class A{
B *b;
public:
void operator= (B *b){
this->b = b;
}
};
B *b = new B()
A *a = new A();
a = b;
I get a "cannot convert B* to A*" error.
Is there a way around this?
Now, if there is a way, and if I use something like:
a = NULL;
Which operator "=" would be used?
You have assigned the pointer instead of the object. Simply replace the last instruction with:
*a = b;
To answer the second question: NULL can be defined in more than one way in the compiler (as of the latest standard, either as the integral 0 or the literal nullptr). Pointers can also be cast to pointer of other types, but passing a void* to an overloaded function that takes an int* or a long* may make the compiler unable to resolve the function being called.
If however, you want to avoid NULL, simply make operator(B& b) instead. References are sure to be pointing at an object.
Your operator= provides assignment from a B* to an A. Your code does not provide a conversion from a B* to a A* (as the error message shows). As such, a=NULL will not use the assignment operator you provided, since a is a pointer, not an A. Your code allows assignment from a B* to an A, like A a= new B();.
If you meant to be using actual objects instead of pointers, remove all the * from your code:
class B{};
class A{
B b;
public:
void operator= (const B& b){ //pass non-primitives by const reference
this->b = b;
}
};
B b;
A a;
a = b;
If you wanted to be using pointers, the only "useful" way to assign a B* to an A* is if a B object derives from A. That appears to not be what you're doing, so assigning a B* to an A* would make no sense in your code.
Change your statement that assigns to a to:
*a = b;