3
#include <cstdio>
#include <cstring>

class A
{
public:
    virtual void foo()
    {
        puts("A");
    }
};

class B : public A
{
public:
    void foo()
    {
        puts("B");
    }
};

int main()
{
    A a;
    B b;
    memcpy(&a, &b, sizeof (b));
    (&a)->foo();
}
kiswa
  • 14,737
  • 1
  • 21
  • 29
Qian
  • 1,462
  • 1
  • 14
  • 25
  • 3
    You know you're blowing the stack to smithereens should `B` ever be bigger than `A`? – Xeo Jun 24 '11 at 13:28
  • 2
    This is UB, but if you were trying to figure out if how hard you can blow your own foot, try with an extra level of indirection: pass the modified `A` object to a function like: `void call( A& x ) { x.foo(); }`, since it is UB you might get anything at all, even `B` being printed... or a crash now or in production code, or... I don't believe in demons but *nasal demons*, those are real! – David Rodríguez - dribeas Jun 24 '11 at 13:35
  • Undefined behavior explains everything. – Qian Jun 24 '11 at 13:51
  • This is almost more C than C++. Yes, there are classes, but cstdio and cstring? Why not use the C++ library? – kiswa Jun 24 '11 at 15:42

6 Answers6

9

Doing raw memory manipulation (such as memcpy) on non-POD types invokes undefined behaviour. You shouldn't do it!

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
4

You are not supposed to mess with non-POD types like that. In particular, the C++ standard says that memcpying non-PODs results in undefined behavior, which, in your case, shows as continuing to see a as being of type A.

In your particular case, the compiler "knows" that the both the "static type" and the "dynamic type" of a is A (since its type can't "legally" change - your trick is illegal), so no virtual dispatch is performed, but a.foo() is called directly (and your trick of overwriting the vptr thus has no effect).

Xeo
  • 129,499
  • 52
  • 291
  • 397
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
3

Because you're riding roughshod over any and all guarantees with your memcpy, and are lucky to get any behaviour at all. Use the assignment operator as you're supposed to!

Pontus Gagge
  • 17,166
  • 1
  • 38
  • 51
3

Because – why should it? The compiler sees that a isn’t a pointer or reference and therefore cannot call anything but the original implementation of foo. The compiler doesn’t bother to make the call virtual (because that is needlessly expensive).

As Oli said, your byte copy provokes undefined behaviour. Anything goes.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
0
memcpy(&a, &b, sizeof (b));
(&a)->foo();

This is undefined behavior. This is guaranteed to work only for POD-types. So... UB is UB. No need to be surprised

Community
  • 1
  • 1
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
0
A a;
    B b;
    memcpy(&a, &b, sizeof (b));

You will have an access violation in this code if A would have an members. Right way is next:

A a;
B b;
A *c = &a;
c->foo(); //A::foo()
c = &b;
c->foo(); //B::foo()
zabulus
  • 2,373
  • 3
  • 15
  • 27