views:

111

answers:

4

Ok I admit it, I'm a total C++ noob.

I was checking the book Data Structures and algorithms in C++ by Adam Drozdek, in the section 1.5 : "Polymorphism" he proposes the next example:

class Class1
{
public:
    virtual void f()
    {
        cout << "Function f() in Class1" << endl;
    }

    void g()
    {
        cout << "Function g() in Class1" << endl;
    }
};

class Class2
{
public:
    virtual void f()
    {
        cout << "Function f() in Class2" << endl;
    }

    void g()
    {
        cout << "Function g() in Class2" << endl;
    }
};

class Class3
{
public:
    virtual void  h()
    {
        cout << "Function h() in Class3" << endl;
    }
};

int main()
{
    Class1 object1, *p;
    Class2 object2;
    Class3 object3;

    p = &object1;

    p->f();
    p->g();

    p = (Class1*)&object2;
    p->f();
    p->g();

    p = (Class1*)&object3;
    p->f();    // Abnormal program termination should occur here since there is 
               // no f() method  in object3, instead object3->h() is called
    p->g();
    //p->h();   h() is not a member of Class1

    return 0;
}

I compiled this using Visual Studio 2010, and two things happened:

  1. First there was no "abnormal termination" in the line p->f()
  2. The method h() of object3 is called in the line where the "abnormal termination" should occur.

The output of the programs is:

Function f() in Class1 Function g() in Class1 Function f() in Class2 Function g() in Class1 Function h() in Class3 Function g() in Class1

I´m trying to understand why this happens, but it seems too strange for me.

Any help would be great.

Thanks in advance!

+12  A: 

If that's really the code the book uses, throw the book in the trash immediately. Polymorphism works over inheritance, such as

class Class2 : public Class1

Without that, there is no hope of a correct program.

The author appears to try and circumvent the requirement (i.e., get an incorrect program to compile) by using

p = (Class1*)&object2;

This is a C-style cast, and it is interpreted as the C++

p = reinterpret_cast< Class1 * >( &object2 );

reinterpret_cast is a dangerous operator, and should never be used between two polymorphic types. This is what should be used:

// use only if dynamic type of object2 is same as static type:
p = static_cast< Class1 * >( &object2 );
// Will not compile unless you add : public Class1

or

// returns NULL if object2 dynamic type not derived from Class1
p = dynamic_cast< Class1 * >( &object2 );

As for the "abnormal termination", I think the author was describing how the code crashed on his machine. However, that's not because the language says it should. He just wrote an incorrect program, which may crash on some machines but not others.

Potatoswatter
Ok thanks, next time I will not listen to Amazon reviews
Mario
I'd say no matter what book you're using, if you're trying to learn OO concepts, C++ is not the language I'd start with. I have no idea what they're using in schools these days, but there were almost riots in the hallways when they changed our "intro to OO" course from Smalltalk to Java, and Java's a sight better then C++ for teaching OO theory...
Kendrick
@Kendrick: I dunno, I learned OO from C++ and no worse for wear. Learning OO and templates simultaneously is a lot to swallow at once, but then you never end up trying to make one look like the other.
Potatoswatter
I tried the code on http://ideone.com/CI26G, it compiles. It looks like through the declaration of h() in Class3 as virtual the "polymorphism" still works, even if there's no such h() function after the cast to Class1. Odd
DaClown
@Potatoswatter: Thanks for the detailed answer, I'll try to find a better book
Mario
Whenever it comes up in a discussion I always suggest C for absolute beginners and C++ to learn OO concepts. They might be harder to learn, but that only means it will take a little more time; the huge benefit is that they will have a good picture of what's going on with machine they are programming. Not so with "easy" languages.
Tamás Szelei
@Potatoswatter I'm not saying you can't do it, but why complicate matters. There are a ton of popular languages that don't require a lot of "strange" syntax or generate weird compiler errors. You can port the knowledge of OO back to C++ easily enough, but not have the extra distractions when you're learning it.
Kendrick
@Tamás Szelei I'd highly recommend C for beginners (since we usually start with proc programming), but what are they going to learn about the underlying machine from C++ that they didn't learn from C? And, given finite time, how much are they going to not learn about OO concepts and abstract architectural principals because they're bogged down with syntax. In the end, of course, it's just personal preference/opinion, and there are reasons to go either way.
Kendrick
You are right about not learning more about the machine. In my opinion, learning OO with C++ will help to understand a widely used implementation of the concept. Not looking at classes as black boxes but knowing how a particular call is resolved be it a virtual or non-virtual member, how inheritance actually works, etc. Different languages might implement OO differently, but the way C++ does is very common.
Tamás Szelei
+1  A: 

h() is probably in the same position in the vtable for Class3 that f() is for Class1 and Class2. The vtable is a table that is stored in your object that contains the addresses of virtual methods, so that the runtime can call the right method even from a pointer of a different type.

This is a weird example without any inheritance. Casts like this aren't exactly safe and I doubt this is portable.

Matt Kane
That seems right, but is really strange to stop the program at p->f() just to see the debugger jump to method h().
Mario
Thanks for your help, Matt
Mario
A: 

Compiler did a compile time binding to the first element of virtual table, since p was of type Class1 it sees that it got method f(). But when actually you run the program it actually gets handle to h() whose address is stored in first position in virtual table.

  • I think this is implementation dependent.
Zimbabao
A: 

This example is abominable and should never have appeared in print. It is not an example of polymorphism.

By casting the objects to unrelated class pointers, he's invoking undefined behavior. Since it's undefined, it's not at all surprising that you get different results than he does.

Mark Ransom
Though I really hate to lose my time with things like this, I still wonder why the book's author would do something like this. It's unsettling. Thanks for your help Mark!
Mario