-1

I've read about reinterpret and dynamic cast, but I saw some examples which I have questions about. reinterpret_cast:

#include <iostream>

using namespace std;

class A
{
public:
    void a() {
        cout << "a";
    }
};
class B: private A
{
public:
    void a() {
        cout << "b";
    }
};


int main()
{
    A *a = new A();
    B *b = reinterpret_cast<B*>(a);
    B *b2 = new B();
    a = reinterpret_cast<A*>(b2);
    b->a();
    a->a();
    return 0;
}

Would print ba. My explanation was that reinterpret_cast change the bit pattern, and both types has a funtion called a() so that was the result. Then I saw this:

using namespace std;
class B;
class A
{
private:
    int j = 4;
public:
    A() {}
    A(const B &b) {}
    void a() {
        cout << j << endl;
    }
};
class B
{
private:
    int i = 5;
public:
    B() {};
    B(const A &a) {}
    void a() {
        cout << i << endl;
    }
};


int main()
{
    A *a = new A();
    B *b = reinterpret_cast<B*>(a);
    B *b2 = new B();
    a = reinterpret_cast<A*>(b2);
    b->a();
    a->a();
    return 0;
}

and that printed 45. I guess it has something with inheritance but I don't know how or why.

About dynamic cast:

#include <iostream>

using namespace std;

class A {
    public:
    virtual ~A(){}
};
class B {
    public:
    void a() {
        cout << "B" << endl;
    }

    virtual ~B() {}
};

int main()
{
    A *a = new A();
    dynamic_cast<B*>(a)->a();
    return 0;
} 

That would print "B". But if I would write:

virtual void a() {
        cout << "B" << endl;
    }

I would get segmentation fault. Why I got the result I got in both examples?

Thanks for all your help!

OferP
  • 373
  • 2
  • 15
  • 4
    Code that has bugs does things you don't expect. Why would you expect code that says "take this pointer to a B and pretend that it's a pointer to an A without applying any conversions needed to handle inheritance" would do anything sensible? And why would you expect a virtual function invoked without any object on which it can be invoked to work? Do things that don't make sense, you get results that don't make sense. – David Schwartz Sep 17 '15 at 11:09

2 Answers2

1

In the first case, you're basically lying to the compiler and telling it to pretend a pointer to an A is a pointer to a B without doing necessary conversions. However, it doesn't matter because the function isn't virtual, so it just calls the function based on the pointer type.

In the second case, the dynamic cast fails because the two types are unrelated. but you still invoke B::a, just on no object. That causes no problem because no attempt to access the object takes place.

In the third case, the dynamic cast fails again. But since the function is virtual, executing it requires accessing the object to determine its fully-derived type. Since there is no object (no instead of a B exists), that fails.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • thanks! about the reinterpret_cast, not sure I got it.. In the first case, does it matter that in both classes it is the first function?? and in the second, if I would do class B: private A I'll get other result. why does the inheritance matter? (notice that both first and second are reinterpret_cast and not dynamic). about the dynamic - can you explain again why if the function is declare virtual its matter? – OferP Sep 17 '15 at 11:36
  • @TonyD Thanks, fixed. – David Schwartz Sep 17 '15 at 11:40
0

The object pointed to has a compile-time type and a run-time type. You need to understand how each of those affects the action taken.

When you do a reinterpret_cast you have changed the compile-time type but have not changed the run-time type.

When you call a non virtual function, which class you get that function from depends only on the compile-time type not the run-time type. That function then assumes the run-time type is either the same as the compile-time type or derived from it. That assumption is false in your first example, which makes the behavior undefined in theory. But in practice, the function doesn't actually use the object, so the object being the wrong type has no consequence.

In your second example, you still call the function in the class of your compile-time type. Your testing hides that fact, so you may be confused about that. But the object is of the run-time type so its data is as initialized in the run-time type. That data is accessed by position, not by name. So the use of i gets the actual value of j (through very undefined behavior) because it has the same position. I expect that caused you to be confused over which function was called. You could make that example easier to understand if you changed:

cout << i << endl;

to

cout << "i == " << i << endl;

In your third example, the dynamic_cast still unconditionally changes the compile-time type to the requested type. But in case the requested type cannot be correctly reached from the run-time type of the actual object (as is true in your example of unrelated classes) the pointer itself is null. So when you then call a function that doesn't actually use the object, the result is technically undefined but in practice executes as the compile-time type. But when you call a virtual function, the call itself uses the object and since the object pointer is null, that will seg fault.

JSF
  • 5,281
  • 1
  • 13
  • 20