By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,921 Members | 1,471 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 434,921 IT Pros & Developers. It's quick & easy.

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

P: n/a
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 '07 #1
Share this Question
Share on Google+
5 Replies


P: n/a
On 13 May, 19:15, johnbrown...@hotmail.com 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

May 13 '07 #2

P: n/a
On May 13, 2:15 pm, johnbrown...@hotmail.com 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.

May 13 '07 #3

P: n/a
On May 13, 1:47 pm, Gavin Deane <deane_ga...@hotmail.comwrote:
>
... 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 '07 #4

P: n/a
On May 13, 2:12 pm, Salt_Peter <pj_h...@yahoo.comwrote:
>
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 '07 #5

P: n/a
On May 14, 6:15 am, johnbrown...@hotmail.com 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.

May 13 '07 #6

This discussion thread is closed

Replies have been disabled for this discussion.