0

I am trying to understand static_cast. I have simple code in which I have two classes. I am assigning one class object to another class object via void pointer.

As per my understanding program should not work

  #include <iostream>

  using namespace std;
  class Test
  {
    public:
          void play(){cout<<"Test::play"<<endl;}
  };
  class Base
  {
    public:

  };

  int main ()
  {
     Base b;
     void* vobj = &b;
     Test *t = static_cast<Test*>(vobj);
     t->play();
     return 0;
  }

After running this program I got output as

  Test::play

Please help me to understand this behavior.

user3013022
  • 196
  • 1
  • 3
  • 16

4 Answers4

3

This is highly dangerous behaviour. You take a pointer (void*) and make it to point to the address of an already constructed object. This is ok. However, you use the pointer to make it a new kind of object ... This is not ok. The memory layout of class Base and Test are the same, but consider the following changes:

  #include <iostream>

  using namespace std;
  class Test
  {
    public:
          Test() : c(1) {}                            // <-- Change here
          void play(){cout<<"Test::play"<< c <<endl;} // <-- Change here
         int c;                                       // <-- here 
  };
  class Base
  {
    public:
       Base() : b(2) {}                          // <- here
    int b;                                  // <- here
 };

 int main ()
 {
    Base b;
    void* vobj = &b;
    Test *t = static_cast<Test*>(vobj);
    t->play();
    return 0;
 }

Do you expecet that it will print 1? No. It will print 2 since it is acting as a Test on the memory of a Base obejct :)

Static cast is not safe and can be misused in situations like this, since no run time check is done whether you are allowed to cast or not :(

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
2

It works because:

  1. The type of variable vobj is void*, and a static cast from void* to Test* is acceptable. Therefore, the compiler does not generate a compilation error for static_cast<Test*>(vobj).

  2. Function play is not virtual in the hierarchy of its class, so the compiler can replace the call to this function with a direct jump to its address without reading it from the V-Table of the calling object first.

  3. Function play does not make use of any member fields of the calling object (this) , so the type of the calling object makes no difference (and in essence, it is exactly like calling a global function).

barak manos
  • 29,648
  • 10
  • 62
  • 114
1

Your program is technically well-formed, although it isn't in practice, of course.

5.2.9/13 states:

A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T,” where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. The null pointer value is converted to the null pointer value of the destination type. A value of type pointer to object converted to “pointer to cv void” and back, possibly with different cv-qualification, shall have its original value.

That is what you are doing. You are tricking the compiler (around two corners) into doing an illegal conversion. The intent of allowing those conversions from void* to another type is of course not to allow you to do any cast (whether it's legal or not), but rather to allow some cases where you legitimately cast a void* to a class knowing that it's correct.

It still "works", or so it seems, but that is just luck (or rather, bad luck... would be better if it crashed).

Damon
  • 67,688
  • 20
  • 135
  • 185
0

In C++, the method call will not check for the object creation. For example, consider the following class

Class Test
{
  public:
    int x;
    void play(int y) 
    { 
      cout<<"X Value: "<<x<<endl; 
      cout<<"Y Value: "<<y<<endl;
    }
}

int main ()
{
   Test t = new Test();
   t->x = 5;
   t->play();
   return 0;
}

In the above code, when I called the play method, internally the method is executed by sending the class instance as a method parameter like below.

void play(Test obj, int y)

If we call the play method without creating an object, it won't fail in the calling line. The c++ call the play method with null parameter (void play(null, 5)). So, the program throws an exception when the method is trying to use the class object to access its member(int x). (Note: If we didn't use any class members in the method, then the method is successfully executed even without creating the object for class).

Consider the below code:

Class Test
{
  public:
    void play(int y) { cout<<"Y Value: "<<y<<endl; }
}

int main ()
{
   Test t;
   t->play(5);
   return 0;
}

In the above example, I called the Play method of Test class without creating the object for the class. Internally play method is called by using null parameter(void play(null, 5)). But, inside the class we didn't used the class object. So, it won't throw any exception and happily printed "Y Value: 5" string and the program will successfully got executed.

Like this, in your code, when you tried to call the play method it will call the method with null parameter. Since, your method is not used any class members your method will got executed successfully. The static_cast<> is not doing any magic here. static_cast<> is used only for the following purposes

  • Converting a pointer of a base class to a pointer of a derived class
  • Convert numeric data types such as enums to ints or ints to floats

In your code, static_cast will not convert the Base class to Test class. It just returned some junk pointer. If you used any class members in the play method definitely the call will get failed in the line where you tried to used the class object.

petchirajan
  • 4,152
  • 1
  • 18
  • 20