Why does this fail without static_cast? - c++

Compiling f works, but compiling g fails with an error.
Why does this happen?
class A {
public:
A() {}
};
class B : public A {
public:
B() {}
};
void f() {
A* a = new A();
B* b = static_cast<B*>(a);
}
void g() {
A* a = new A();
B* b = a;
}

A static_cast forces a conversion that is potentially unsafe.
B* b = static_cast<B*>(a);
This would be valid if a pointed to an A object that actually was the base class sub-object of a B object, however it doesn't. The cast forces the conversion.
B* b = a;
There is no cast here and there is (correctly) no implicit conversion allowed from base class pointer to derived class pointer. A pointer to a derived class can always be converted to a pointer to a base class because a derived class object always contains a base class sub-object but not every base class instance is a sub-object of a particular derived class type.

Well, yeah. Doing:
B* b = new A();
Is unsafe. You end up with a B pointer to an A object; you never construct the B portion of the object; your object is "sliced".
On the other hand...
A* a = new B();
...would be fine.

You are trying to convert a pointer from A* to B*. I am not sure what you are trying to achieve. But since B* is derived from A* and not the other way around this is not valid. Maybe you want to do something like this:
int main()
{
///The above code compiles while if I replace above two line in main with below assignment it gives error.
A *a=new A();
A * b=new B();
}

Yes, it does give an error if you want to assign a base class to a derived class pointer type. No, it doesn't give an error if you explicitly cast the pointer type, because in C++ you are allowed to shoot yourself in the foot if you so desire.
What exactly is baffling you, or what did you expect to achieve with your code?

A base class cannot be implicitly converted to a derived class. Just consider this
class A {
public: int x;
};
class B : public A {
public: int y;
};
B* b = new A; // assume it works, so 4 bytes is allocated and initialized.
b->y; // bam! accessing unallocated region.

Related

Casting pointer to different pointer causes wrong virtual function to be called

#include <iostream>
struct A {
virtual void a() {
puts("A");
}
};
struct B {
virtual void b() {
puts("B");
}
};
struct C {
virtual void c() {
puts("C");
}
};
struct D : public A, public B, public C {
virtual void c() {
C::c();
puts("cd");
}
};
int main() {
A* obj = new D;
obj->a();
B* b = (B*)obj;
b->b();
C* c = (C*)obj;
c->c();
return 0;
}
I have this code where I have non virtual multiple inheritance. However, it seems to call the wrong virtual function when I call the functions in the main function.
Instead of outputting:
A
B
C
cd
It outputs:
A
A
A
What puzzles me is that when I change the code to doing this:
B* b = (B*)(D*)obj;
b->b();
C* c = (C*)(D*)obj;
c->c();
It outputs what I would expect (see above). Afaik doing a double pointer cast like this wouldn't effect anything and would be optimized out by the compiler. But it seems to be changing what virtual function is being called.
Can someone explain why this would change what virtual function is being called?
Notes:
I printed the pointers at each step, they are the same.
I want to avoid using dynamic_cast (although it does work) as it's too slow for what I need it to do.
Can someone explain why this would change what virtual function is being called?
Generally, a C-style cast between pointer types won't change the value of the pointer and so will have no effect. There is, however, one exception.
A cast between a class and a parent or child class can change the value of the pointer. For example:
class A
{ int a; };
class B
{ int b; };
class C : public A, public B
...
Now, a pointer to an instance of class A will probably have the same value as a pointer to its a member and a pointer to an instance of class B will probably have the same value as a pointer to its b member. A pointer to an instance of class C can't have the same value as a pointer to both its A::a and its B::b members since they're distinct objects.
A function expecting a B* can be passed a C* since a C is a B. Similarly, a function expecting an A* can be passed a C* for the same reason. But at least one of these will require a value change to the pointer.
So casts between these types will change the values, the others are all no-ops.
Of course, all of this is UB. You are casting between unrelated types and then dereferencing them.
I want to avoid using dynamic_cast (although it does work) as it's too slow for what I need it to do.
That seems very hard to believe.

Slicing vs Upcasting :: Is my understanding correct?

Let A be the base class and B be it's publicly derived class.
B b;
Slicing:
A a = b;
Upcasting:
A* p = &b; // p is a pointer variable of type A
A& r = b; // r is a reference variable of type A
Is this correct? Please share similar examples to illustrate the two concepts if possible.
Yes!
Object slicing happens when a derived class object is assigned to a base class object, additional attributes of a derived class object are sliced off to form the base class object.
So yes, if you have a base class A
class A{
public:
int x;
char y;
};
and a class B derived publically from A with some extra data members,
class B:public A{
public:
int z;
};
doing A a = b; will slice off 'z'.
Upcasting is conversion of pointer or reference of derived class type to pointer or reference of base class type, going up in the inheritance tree.
B objB;
A *objA = &objB;
Just to bring more light on this subject, you can convert a base-class pointer(reference) to a derived-class pointer (reference).It is called downcasting(opposite to upcasting).
B *objB = (B *) &A;
But there's no way you can assign a base class object to a derived class object.
Cheers!

Upcasting pointers

I have a doubt with upcasting with pointers in C++.
I'm going to write an example of my problem:
class A {}
class B : public A {}
A* pA = new A();
B* pB = new B();
pA = pB; //fails
pA = dynamic_cast<A*>(pB); //fails
I don't know what I'm missing. I think I don't understand at all the upcasting. Any help please? Thanks
UPDATED With the error:
[exec] ..\asdf\qwerty.cpp(123) : error C2681: 'B*' : invalid expression type for dynamic_cast
I have found how it works, like this:
pA* = (pA*)pB;
But I don't understand why.
EDIT: My editor is telling me that: "a value of type B* cannot be assigned to an entity of type A*". What does this mean?
To be more exactly, pB is being returned by a function. I don't know if it has something to do: is like this:
class C {
B* pB;
B* getB() { return pB; }
}
A* pA;
pA = c.getB(); //this crashes. c was declared before... it is just an example
You are missing semicolons in your classes definition. Upcasting in C++ is totally legal and can be done in an implicit way (polymorphism). This works for me :
class A {
public:
virtual ~A() {}
};
class B : public A {};
int main()
{
A* pA;
B* pB = new B();
pA = pB;
delete pB;
return (0);
}
Also, you should declare the destructor of A as virtual to avoid potential memory leaks. If you do not and try to delete the instance of B, the destructor of A will be called, but the destructor of B will not be called, leaving all the allocated resources held by B unfreed.
You are missing semicolons ; after class definitions:
class A {};
class B : public A {};
Also for dynamic_cast to return a meaningful result you need at least one virtual method in A. You need to have virtual destructor in a polymorphic base class for destruction to work correctly anyway:
class A {
public:
virtual ~A() {}
};
no reason for pA = pB; not working, it should work, what do you mean by "fails"?
pointer upcast is trivial and can be used without dynamic casting.

How to call function from inherited class?

I have the code:
class A{ //base class
public:
virtual std::string getString(){return "class A";}
};
class B: public A{
public:
std::string getString() {return "it is B class";}
};
class C{
public:
C(){
B b;
a = b;
}
std::string test() {return a.getString();}
private:
A a;
};
int main()
{
C c;
std::cout << c.test();
return 0;
}
c.test() says "class A", but how I can call method getString() from class B and not A?
Thanks!
The problem is, your B object gets sliced when assigned to an A object. This is because you assigned by value, not by reference or pointer. Since you declared a like this
A a;
what happens during the assignment a = b is that the actual state of b is copied over into a. However, since a is a value object, only the A part of object b is copied, and its "B-ness" is completely lost!
To avoid this, you need to declare a as a pointer type, as suggested by others (a reference would also work, but then you would need to considerably rewrite your example, since you can't assign to references, only initialize them). If a is a pointer (A*), the assignment a = b makes a point to the object represented by b, which is still a B object, thus you will observe the polymorphic behaviour you expected. However, in this case, you must ensure that b stays alive even after exiting the constructor - otherwise you leave a dangling reference which causes undefined behaviour (read: bad things you don't want to happen) when dereferenced.
Since a pointer example was already shown by #Nawaz, I will give another using a reference:
class C{
public:
C() : a(b) { // references must be initialized in the constructor initializer list
}
std::string test() {return a.getString();}
private:
B b; // moved to class scope to ensure that it stays alive
A& a;
};
You need to implement like this:
class C{
public:
C(){
a = new B;
}
std::string test() {return a->getString();}
private:
A *a;
};
This will call getString() from class B and not A.
What you're trying to do is called "dynamic polymorphism" which is achieved through pointer (or reference) of type base class (which is A), but the pointer points to an object of type derived class (which is B).
Because your member a is not an A*, it is an A instance. Therefore you are just assigning the A part of B to variable a. if you convert a to an A*, you will get the expected result.
You are slicing therefore it will not work. a is an A it is not a B.
To work your class member variable a must be a pointer or a reference.
As a pointer
class C{
public:
C(){
a = new B;
}
std::string test() {return a->getString();}
private:
A *a;
};
As a reference
class C{
public:
C() : a( *(new B) )
{
}
std::string test() {return a.getString();}
private:
A &a;
};
Of course the code I have produced leaks but will work with the virtual function.

Pointer to base class

If I have the following classes:
class A
{
...
}
class B
{
...
}
class C : public A, public B
{
...
}
and somewhere I detect that the pointer of class B that I have actually points to a class C, but a function requires a pointer to class A, what can I do to get that pointer to class A?
If you know for certain that you have a B* that points to a C object, you can use a pair of static_casts:
B* bp = new C();
C* cp = static_cast<C*>(bp);
A* ap = static_cast<A*>(cp);
The only way to cast across the inheritance hierarchy is to use dynamic_cast, which requires that the type is polymorphic (that is, your class must have at least one virtual member function; since your base class destructors should be virtual, this usually isn't a problem):
B* bp = new C();
A* ap = dynamic_cast<A*>(bp);
dynamic_cast has the added benefit that if it fails (that is, if bp doesn't actually point to a C), it returns NULL. It has the disadvantage of a slight performance cost (static_cast is effectively free at runtime).
The code
class A
{
};
class B
{
};
class C : public A, public B
{
};
int main() {
C c;
A *a = &c;
}
is valid since C is already an A, so the assignment is valid.
If C inherits from A as you have shown, then a C* pointer should be implicitly convertible to an A* pointer. Is it possible that you haven't included the declaration of class C, so that the compiler isn't aware of this inheritance relationship? Or that there is actually a different inheritance relationship than that given in your question? Some code would be helpful in diagnosing this problem.
Edit
Based on the updated version of your question:
// Converts b to type A*, but only if it is actually
// of type C; otherwise, returns NULL
A* convertBtoAviaC(B* b) {
C* c = dynamic_cast<C*>(b);
return c; // note may be NULL, if b is not a C
}