Reputation: 687
is it legal to cast between pointers on classes that have common ancestor? Does the compiler notice such hierarchy and makes sure its safe (call 1) ? Or does the user have to go through the hierarchy manually for it to be always safe (call 2) ?
say we have
class A{};
class B:A{};
class C:A
{
public:
int SomeFunc(){return 3;}
};
int _tmain(int argc, _TCHAR* argv[])
{
B* b = (B*)((A*)new C()); // this is done manually, i believe it is safe
((C*)b)->SomeFunc(); // is this safe? this is the cast in question
return ((C*)((A*)b))->SomeFunc(); // this is done manually, i believe it is safe
}
edit: Made this code compilable and runnable
edit2: Added more comments
Upvotes: 5
Views: 3658
Reputation: 60979
B* b = (B*)((A*)new C()); // this is done manually, i believe it is safe
This is not safe.
Casts of the form (T)
expr are, roughly speaking, converted into either static_cast
or reinterpret_cast
. [expr.cast]/4:
The conversions performed by
- a
const_cast
(5.2.11),- a
static_cast
(5.2.9),- a
static_cast
followed by aconst_cast
,- a
reinterpret_cast
(5.2.10), or- a
reinterpret_cast
followed by aconst_cast
,can be performed using the cast notation of explicit type conversion. The same semantic restrictions and behaviors apply […]
If a conversion can be interpreted in more than one of the ways listed above, the interpretation that appears first in the list is used, even if a cast resulting from that interpretation is ill-formed.
You can ignore const_cast
here as no qualification conversions are done in your code.
static_cast
suffices in both casts, the first one, (A*)
, and the second one, (B*)
.
The first one is just fine. Upcasting is never an issue.
The second one induces undefined behavior. [expr.static.cast]/11:
A prvalue of type “pointer to cv1
B
,” whereB
is a class type, can be converted to a prvalue of type “pointer to cv2D
”, whereD
is a class derived (Clause 10) fromB
, if a valid standard conversion from “pointer toD
” to “pointer toB
” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, andB
is neither a virtual base class ofD
nor a base class of a virtual base class ofD
. […] If the prvalue of type “pointer to cv1 B” points to aB
that is actually a subobject of an object of typeD
, the resulting pointer points to the enclosing object of typeD
. Otherwise, the result of the cast is undefined.
Note also that just because the static_cast
triggers UB that doesn't mean it isn't selected (and replaced by reinterpret_cast
).
The second and third casts base on the first one (which causes undefined behavior), thus talking about their validity is pointless.
Upvotes: 2
Reputation: 36401
Your casts are both legal and correct but very dangerous. You should use reinterpret_cast<> to tag them in your code.
You can always cast any address of any type A
to any other address of any type B
and get your first address back. This is essentially what you've done:
A *pa = &some_a;
B *pb = reinterpret_cast<B *>(pa);
pa = reinterpret_cast<A *>(pb);
and then dereference pa
. This example works but it is so easy to make a mistake...
Upvotes: 0
Reputation: 16099
Unless you really really know what your doing, don't do that.
The casts are legal, but using them on anything but the correct class results in undefined behaviour, any use of b
without further casts results in UB, which might work, do nothing or start WWIII.
The casts simply tells the compiler that is should consider the variable to be of another type (unless it is multiple inheritance), but as soon as the cast variable is used it must actual be legal to use it in the way the code does, using B's function table is no good if the object is a C or vice versa. As this is undefined behaviour the compiler might emit whatever code it feels is right.
Example
class AA { };
class BB { };
class CC : public AA, public BB { };
int main () {
CC cc; // address is 0x22aa6f
BB* bb = &cc; // bb now is 0x22aa6f
cout << &cc << "," << bb << "\n";
return EXIT_SUCCESS;
}
Gives
0x22aa6f,0x22aa6f
Example with multiple inheritance
class AX{ int a = 1; };
class BX{ int b = 2; };
class CX:AX,BX {
public:
int c = 3;
int SomeFunc(){cout << "SomeFunc " << c << " "; return c;}
};
int cast() {
CX* c;
BX* b = (BX*)((AX*)(c = new CX())); // this is done manually, i believe it is safe
cout << "c=" << c << ", b=" << b << ", cx=" << ((CX*)b) << ", ca=" << ((CX*)((AX*)b)) << endl;
((CX*)b)->SomeFunc(); // is this safe? this is the cast in question
return ((CX*)((AX*)b))->SomeFunc(); // this is done manually, i believe it is safe
}
int main () {
return cast();
}
output
c=0x60003ac70, b=0x60003ac70, cx=0x60003ac6c, ca=0x60003ac70
SomeFunc 2 SomeFunc 3
new
The 2 calls to SomeFunc works despite the wrong address
this
pointerthis
is not used it works, so I had to add some use.To check if it is safe you need to use dynamic_cast.
int main() {
A* AP = new C();
C* CP = dynamic_cast<C*>(A);
if (CP != nullptr)
CP->SomeFunc();
return EXIT_SUCCESS;
}
Upvotes: 2
Reputation: 10733
To check whether a cast is meaningful or not just use dynamic_cast. dynamic_cast will cast properly if cast is safe OR returns NULL (in case of pointers, for references it throws bad_cast exception ) if its not able to cast to target type.
For your question just think about whether this cast is meaningful. You are casting B class to C where these classes have no knowledge of each other. So, surely this cast would fail.
You are doing:-
B* b = (B*)(new C());
This would fail(means won't even compile ) if given to dynamic_cast since classes involved are not polymorphic. Even if you make class B polymorphic cast would fail. Leave further casting.
One more thing you can cross cast using dynamic_cast safely assuming classes are polymorphic and cast is safe. For e.g:-
class A;
Class B;
Class C : public A, public B
A *a = new C;
You can cast this to sibling:-
B *b = dynamic_cast<B*> (a);
Upvotes: 1