Bogus cast from class to totally unrelated class works - why?

Discussion in 'C++' started by johnbrown105@hotmail.com, May 13, 2007.

  1. Guest

    Hello All,

    I did try searching the archives, but the topics seemed to be mostly
    about base
    and derived classes.

    I have reached Chapter 8 in Bruce Eckel's "Thinking in C++ 2nd Edition
    Vol. 1".
    Question 28 says:

    "Create a class called bird that can fly( ) and a class rock that
    can't. Create
    a rock object, take its address, and assign that to a void*. Now take
    the void*,
    assign it to a bird* (you'll have to use a cast), and call fly( )
    through that
    pointer. Is it clear why C's permission to openly assign via a void*
    (without a cast) is a "hole" in the language, which couldn't be
    propagated
    into C++?"

    I came up with:

    #include <iostream>
    using namespace std;

    class Bird{
    public:
    void fly(){ cout << "Flying..." << endl; }
    };

    class Rock{
    double d;
    };

    int main()
    {
    Rock r;
    void *v = &r;
    Bird *pb = reinterpret_cast<Bird*>(v);
    pb->fly();
    return 0;
    }

    This compiled and linked without warnings, as I expected. I understand
    why
    you should not be allowed to convert a Rock to a Bird (unless you
    really, really
    want to very badly).

    What I did *not* expect was that it would run, but it did. Why don't
    I get
    a run-time error when I call fly() in my Rock masquerading as a Bird?

    I tried it with gcc 3.4.2 (MingW Windows port) and 4.1.1
    (http://oss.netfarm.it/mplayer-win32.php) and it worked in both cases.
    I am on Windows XP SP2.
    , May 13, 2007
    #1
    1. Advertising

  2. Gavin Deane Guest

    On 13 May, 19:15, wrote:
    > Hello All,
    >
    > I did try searching the archives, but the topics seemed to be mostly
    > about base
    > and derived classes.
    >
    > I have reached Chapter 8 in Bruce Eckel's "Thinking in C++ 2nd Edition
    > Vol. 1".
    > Question 28 says:
    >
    > "Create a class called bird that can fly( ) and a class rock that
    > can't. Create
    > a rock object, take its address, and assign that to a void*. Now take
    > the void*,
    > assign it to a bird* (you'll have to use a cast), and call fly( )
    > through that
    > pointer. Is it clear why C's permission to openly assign via a void*
    > (without a cast) is a "hole" in the language, which couldn't be
    > propagated
    > into C++?"
    >
    > I came up with:
    >
    > #include <iostream>
    > using namespace std;
    >
    > class Bird{
    > public:
    > void fly(){ cout << "Flying..." << endl; }
    >
    > };
    >
    > class Rock{
    > double d;
    >
    > };
    >
    > int main()
    > {
    > Rock r;
    > void *v = &r;
    > Bird *pb = reinterpret_cast<Bird*>(v);
    > pb->fly();
    > return 0;
    >
    > }
    >
    > This compiled and linked without warnings, as I expected. I understand
    > why
    > you should not be allowed to convert a Rock to a Bird (unless you
    > really, really
    > want to very badly).
    >
    > What I did *not* expect was that it would run, but it did. Why don't
    > I get
    > a run-time error when I call fly() in my Rock masquerading as a Bird?
    >
    > I tried it with gcc 3.4.2 (MingW Windows port) and 4.1.1
    > (http://oss.netfarm.it/mplayer-win32.php) and it worked in both cases.
    > I am on Windows XP SP2.


    Well the behaviour is undefined so the compiler is allowed to do
    anything it wants and from a language definition point of view no
    outcome is more or less valid than any other. But what is probably
    happening is:

    fly() is a member function of Bird and so your compiler creates code
    exactly as it would for a normal function, except it inserts an extra
    parameter of type Bird*. This extra parameter is the implied this
    pointer that every (non-static) member function of any class has but
    that does not appear explicitly in the parameter list. Now, in your
    code, the Bird* this pointer inside fly() actually points to a Rock
    and so any attempt to use or dereference the pointer isn't going to
    work. But none of the code in your fly() function actually uses the
    this pointer - no other member functions of Bird are called and no
    member data is touched. Try changing the definition of Bird as below
    and you might see something more obviously strange. Watch out for
    nasal demons.

    class Bird{
    public:
    Bird() : wbpm(42) {}
    void fly()
    {
    cout << "Flying at " << this->wbpm << " wing beats per
    minute" << endl;
    }
    private:
    int wbpm;
    };

    Gavin Deane
    Gavin Deane, May 13, 2007
    #2
    1. Advertising

  3. Salt_Peter Guest

    On May 13, 2:15 pm, wrote:
    > Hello All,
    >
    > I did try searching the archives, but the topics seemed to be mostly
    > about base
    > and derived classes.
    >
    > I have reached Chapter 8 in Bruce Eckel's "Thinking in C++ 2nd Edition
    > Vol. 1".
    > Question 28 says:
    >
    > "Create a class called bird that can fly( ) and a class rock that
    > can't. Create
    > a rock object, take its address, and assign that to a void*. Now take
    > the void*,
    > assign it to a bird* (you'll have to use a cast), and call fly( )
    > through that
    > pointer. Is it clear why C's permission to openly assign via a void*
    > (without a cast) is a "hole" in the language, which couldn't be
    > propagated
    > into C++?"
    >
    > I came up with:
    >
    > #include <iostream>
    > using namespace std;
    >
    > class Bird{
    > public:
    > void fly(){ cout << "Flying..." << endl; }
    >
    > };
    >
    > class Rock{
    > double d;
    >
    > };
    >
    > int main()
    > {
    > Rock r;
    > void *v = &r;
    > Bird *pb = reinterpret_cast<Bird*>(v);
    > pb->fly();
    > return 0;
    >
    > }
    >
    > This compiled and linked without warnings, as I expected. I understand
    > why
    > you should not be allowed to convert a Rock to a Bird (unless you
    > really, really
    > want to very badly).


    You aren't converting a Rock to a bird, you are 'reinterpreting' a
    Rock* to a void* and that to a Bird*.
    Converting a pointer converts the pointer, not its pointee.
    Did you not read the warnings about using reinterpret_cast?
    An old type cast might very well apply a static_cast, a dynamic_cast
    or a reinterpret_cast.
    You had no control over which was employed. Now you do.

    int main()
    {
    Rock r;
    Bird *pb = static_cast<Bird*>(&r); // invalid cast
    pb->fly();
    return 0;
    }

    Again, its the pointer being cast, not the object. That Rock is still
    a Rock regardless of which type cast you apply on the ptr. I think you
    are trying to bite more than you can chew here.

    >
    > What I did *not* expect was that it would run, but it did. Why don't
    > I get
    > a run-time error when I call fly() in my Rock masquerading as a Bird?


    Because its not a Rock* anymore. Unfortunately, the fly() member
    function is not accessing the object in any way, which would generate
    an error.
    Classic example:

    #include <iostream>

    class Rock
    {
    double d;
    public:
    Rock( double d_ ) : d( d_ )
    { }
    double get
    () const
    {
    return d;
    }
    void foo()
    {
    std::cout << "Rock::foo()";
    }
    };

    int main()
    {
    Rock * p;
    p->foo(); // no error
    p->get(); // seg fault
    }

    Why, cause there is no Rock object to access. All you have is an
    unitialized pointer.

    >
    > I tried it with gcc 3.4.2 (MingW Windows port) and 4.1.1
    > (http://oss.netfarm.it/mplayer-win32.php) and it worked in both cases.
    > I am on Windows XP SP2.
    Salt_Peter, May 13, 2007
    #3
  4. Guest

    On May 13, 1:47 pm, Gavin Deane <> wrote:
    >
    > ... Now, in your
    > code, the Bird* this pointer inside fly() actually points to a Rock
    > and so any attempt to use or dereference the pointer isn't going to
    > work. But none of the code in your fly() function actually uses the
    > this pointer - no other member functions of Bird are called and no
    > member data is touched. Try changing the definition of Bird as below
    > and you might see something more obviously strange. Watch out for
    > nasal demons.
    >

    <cut>

    I see. I now get:
    Flying at 2009312941 wing beats per minute

    I was still expecting something more dramatic than that, but it will
    do. Thanks.
    , May 13, 2007
    #4
  5. Guest

    On May 13, 2:12 pm, Salt_Peter <> wrote:
    >


    > You aren't converting a Rock to a bird, you are 'reinterpreting' a
    > Rock* to a void* and that to a Bird*.
    > Converting a pointer converts the pointer, not its pointee.


    I understand. My language was imprecise.

    > Did you not read the warnings about using reinterpret_cast?


    Most certainly.
    >
    > Again, its the pointer being cast, not the object. That Rock is still
    > a Rock regardless of which type cast you apply on the ptr.


    I may be a newbie, but I am not *that* new. I would like to take this
    opportunity to remind you of my post's subject line, which is:
    *BOGUS CAST ... WORKS - WHY?* I know that my Rock is still
    a Rock. I just wanted to know why my Rock did not sink like a
    stone, as a proper Rock should have.


    > I think you
    > are trying to bite more than you can chew here.
    >


    I am doing nothing of the kind. I am doing an exercise from a
    textbook, the entire point of which is to show:

    1) what can happen when you perform bogus casts, and
    2) that C allows assignments to and/or from void *
    without a cast, while C++ does not.

    If you had bothered to read my post, you would have seen that.
    Then again, you didn't even read the subject line. No matter
    how anxious you are to attack hapless newbies, you should
    at least read the post first.

    >
    >
    > > What I did *not* expect was that it would run, but it did. Why don't
    > > I get
    > > a run-time error when I call fly() in my Rock masquerading as a Bird?

    >
    > Because its not a Rock* anymore. Unfortunately, the fly() member
    > function is not accessing the object in any way, which would generate
    > an error.


    Which is the useful answer to my query.Thank you.

    > Classic example:
    >

    <Classic example cut>
    It crashes at run-time. Now that's more like it.
    , May 13, 2007
    #5
  6. Old Wolf Guest

    On May 14, 6:15 am, wrote:
    > What I did *not* expect was that it would run, but it did. Why don't
    > I get a run-time error when I call fly() in my Rock masquerading as a Bird?


    Your expectation is wrong. When you write code that
    is compilable but has bogus effects, anything could
    happen. Maybe you will get a runtime error but maybe
    not. Often it *appears* to work normally, as in your
    example, but then fails just when you have to do a
    demonstration of your code.
    Old Wolf, May 14, 2007
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. BernieH
    Replies:
    2
    Views:
    327
    Travis Newbury
    Dec 2, 2004
  2. Alf P. Steinbach

    dynamic_cast between unrelated types

    Alf P. Steinbach, Apr 5, 2005, in forum: C++
    Replies:
    8
    Views:
    1,296
    Karl Heinz Buchegger
    Apr 5, 2005
  3. Mr. SweatyFinger
    Replies:
    2
    Views:
    1,744
    Smokey Grindel
    Dec 2, 2006
  4. Tastic
    Replies:
    2
    Views:
    283
    Tastic
    Feb 16, 2007
  5. James Irvine
    Replies:
    3
    Views:
    368
    Alvin Bruney [MVP]
    Jul 25, 2007
Loading...

Share This Page