Casting between two derived classes

Is it possible to cast between pointers to classes that have a common ancestor? Does the compiler mark such a hierarchy and guarantee its safety (call 1)? Or does the user have to traverse the hierarchy manually so that it is always safe (challenge 2)?

let's say that

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 compileable and executable

edit2: more comments added

+3


source to share


4 answers


B* b = (B*)((A*)new C()); // this is done manually, i believe it is safe

      

It is not safe .
Letters of the form (T)

expr are roughly converted to static_cast

or reinterpret_cast

. [Expr.cast] / 4:

Conversions performed by

  • a const_cast

    (5.2.11),
  • a static_cast

    (5.2.9),
  • a static_cast

    followed by const_cast

    ,
  • a reinterpret_cast

    (5.2.10) or
  • a reinterpret_cast

    followed by const_cast

    ,

can be performed using an explicit type conversion letter. The same semantic constraints and behavior are used [...]
If a transformation can be interpreted in more than one of the ways listed above, the interpretation that appears first in the list is used, even if the cast resulting from that interpretation is ill-formed.

You can ignore this here const_cast

as no conversions are done in your code. static_cast

missing in both throws, the first one (A*)

, and the second - (B*)

. The first one is just fine. Acceleration is never a problem.
The second invokes undefined behavior. [Expr.static.cast] / 11:



The class value "pointer to cv1 B

", where B

is the type of the class, can be converted to a prvalue of type "pointer to cv2 D

", where D

is the derived class (clause 10) from B

, if a valid standard conversion from "pointer to D

" to "pointer to B

" exists (4.10), cv2 is the same cv qualification as a higher cv qualification than cv1 and B

is neither a virtual base class D

nor a virtual base class base class D

. [...] If a value of type "pointer to cv1 B" points to a B

, which is actually a subobject of the type object D

, the resulting pointer points to an object of type D

.Otherwise, the result of the cast is undefined.

Note that just because it static_cast

launches UB, which does not mean that it is not selected (and replaced with reinterpret_cast

).

The second and third fundamentals are based on the first (which causes undefined behavior), so it makes no sense to talk about their reliability.

+2


source


If you really don't know what you are doing, don't do it.

The assignments are legal, but using them on anything other than the correct class results in undefined behavior, any use b

with no further results results in UB being able to run, do nothing, or start WWIII.

The casts just tell the compiler to take into account that the variable is of a different type (unless it is multiple inheritance), but once a cast variable is used, it must be legal to use it in the way the code does using the B function table is not good if the object is C or vice versa. Since this behavior is undefined, the compiler can emit whatever code it thinks is correct.

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

  • c - valid address new

  • b is a cast to AX first and then to BX (which doesn't make any sense for AX), but b is just set to the same address as c
  • cx is interpreted as CX, multi-inheritance inheritance, and indeed changes the address as if b were the second class in inheritance.
  • ca is the correct reinterpretation of b through AX and then CX.

2 calls to SomeFunc work despite the wrong address

  • a function call was found via the current type, which is CX due to the last press.
  • invalid addresses are passed as a this

    pointer
  • because this

    not used, so I had to add some usage.
  • we have now introduced undefined behavior due to the cast (BX *) ((AX *) c, which makes casting (CX *) b do the wrong thing.

To check if it's 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;   
}

      

+2


source


To check if the accent is significant or not just use dynamic_cast. dynamic_cast will cast correctly if the cast is safe, or returns NULL (in the case of pointers, for references it throws a bad_cast exception) if it cannot use the target type.

For your question, just think about whether this actor is significant. You are throwing class B to C where these classes don't know each other. So, of course, this cast will not work.

You do: -

B* b = (B*)(new C());

      

It won't work (the tool won't even compile) if set to dynamic_cast, since the classes involved are not polymorphic. Even if you do a polymorphic cast of class B, you will fail. Leave further casting.

Another thing you can cross-use using dynamic_cast of the intended classes safely is polymorphic, and casting is safe. For example for example: -

class A;
Class B;
Class C : public A, public B

A *a = new C;

      

You can give it to your brother: -

B *b = dynamic_cast<B*> (a);

      

+1


source


Your throws are legal and correct, but very dangerous. You have to use reinterpret_cast <> to tag them in your code. You can always specify any address of A

any other address of any type B

and return your first address. This is essentially what you did:

A *pa = &some_a;
B *pb = reinterpret_cast<B *>(pa);
pa = reinterpret_cast<A *>(pb);

      

and then act out pa

. This example works, but it's so easy to make a mistake ...

0


source







All Articles