|
-
July 14th, 2005, 10:56 PM
#1
Casting - Is this allowed
The following works on my compiler (VC 6.0), but is it allowed by the standard?
Converting Derived* → Base* works OK; why doesn't Derived** → Base** work?
If you see in the code, he says that it is an error in C++ to do this...
Code:
Vehicle** vehiclePtrPtr = carPtrPtr;
but this works fine...
Code:
Vehicle** vehiclePtrPtr = (Vehicle**)carPtrPtr;
Is it allowed by the standard.
C++ program ran... C++ program crashed... C++ programmer quit !!   
Regards
Shaq
-
July 15th, 2005, 01:51 AM
#2
Re: Casting - Is this allowed
No, it doesn't "work fine". Read the following:
http://www.parashift.com/c++-faq-lit...html#faq-27.11
Regards,
Paul McKenzie
-
July 15th, 2005, 04:32 AM
#3
Re: Casting - Is this allowed
It is not valid!
Let me explain:
A pointer to Derived can be cast to a pointer to Base.
It is an universal rule.
With most compilers (even if it is not specified by the standard, and is an implementation detail) casting a pointer to Derived to a pointer to Base, is done by doing nothing in simple inheritance cases:
That is:
Code:
class A
{
public:
int x,y;
};
class B:public A
{
public:
int anInteger;
};
In this simple case, casting from B* to A* is trivial for most compilers!
But with multiple inheritances this cannot be done!
Code:
class A
{
public:
int x,y;
};
class B
{
public:
int w;
};
class C:public A,public B
{
};
It is probable (with common implementations) that casting from C* to A* is just done without doing anything, but cast from C* to B* is done (with common implementations) by adding sizeof(B) to the C* pointer.
So, you understand that the standard can just says that Derived* can be cast to Base*, but cannot be interpreted as a pointer to Base*.
So, if you want to correctly cast a Derived** (a pointer to an array of Derived*) to a Base** (an pointer to an array of Base*), it needs casting each array item (Derived*) to a Base*.
But it cannot be done automatically, because the compiler cannot know how many items there are in the array, moreover you can use only half of these items, and let other items uninitialized, so the compiler cannot even know which pointers are valid, and which pointers are not.
Moreover the compiler could only do in-place cast, or creating a new array will produce memory leaks.
It is easy to understand that it is really totally impossible to do such conversion!
But this code:
Code:
Vehicle** vehiclePtrPtr = (Vehicle**)carPtrPtr;
Compiles correctly, because the C-style cast is probably interpreted as a reinterpret_cast, and reintepret_cast can be done from any integral or pointer to type to any other integral or pointer type (except if it removes some const or volatile attributes).
In fact, it is probable that this code (non-portable) will work, because if Car is just derived from Vehicle with standard simple-inheritance, it will works with most compiler implementations.
But you must know that it is not portable!
-
July 15th, 2005, 05:34 AM
#4
Re: Casting - Is this allowed
To sum up it is not correct because a Derived* is not a kind of Base* (even though a Derived is a kind of Base).
A Base* can be pointed to a different Base* that is not a Derived *. And a Derived* cannot. So you can do something with a Base* that you can't do with a Derived* which breaks a fundamental law of polymorphism.
Now a Base* const is a type of Derived* const because it cannot be reseated to point to a different Base* object, and there is nothing you can do with it except call methods of your Base object, something you can always do with a Derived* const.
(That is a Base* const, not a const Base*).
Therefore you should be able to cast a Derived * const * to a Base * const *.
-
July 15th, 2005, 07:36 AM
#5
Re: Casting - Is this allowed
 Originally Posted by NMTop40
To sum up it is not correct because a Derived* is not a kind of Base* (even though a Derived is a kind of Base).
A Base* can be pointed to a different Base* that is not a Derived *. And a Derived* cannot. So you can do something with a Base* that you can't do with a Derived* which breaks a fundamental law of polymorphism.
Now a Base* const is a type of Derived* const because it cannot be reseated to point to a different Base* object, and there is nothing you can do with it except call methods of your Base object, something you can always do with a Derived* const.
(That is a Base* const, not a const Base*).
That is true
 Originally Posted by NMTop40
Therefore you should be able to cast a Derived * const * to a Base * const *.
But, that is false!
Look at my previous post. You need a conversion from Derived* to Base*, the compiler cannot just interpret a Derived* as Base*, except with the common implementation of non-virtual single inheritance.
For example:
Code:
#include <iostream>
using namespace std;
class Base
{
int h;
public:
void f() {cout<<h<<" ";}
Base():h(1) {}
};
class Base2
{
int x;
public:
Base2():x(2) {}
};
class Derived:public Base2,public Base
{
};
int main()
{
Derived x;
Derived *p=&x;
Derived * const *pp=&p;
(*reinterpret_cast<Base * const *>(pp))->f(); // outputs 2 with BC++ or GCC, but it produces UNDEFINED results!
return 0;
}
Code:
#include <iostream>
using namespace std;
class Base
{
int h;
public:
void f() {cout<<h<<" ";}
Base():h(1) {}
};
class Derived:public Base
{
};
int main()
{
Derived x;
Derived *p=&x;
Derived * const *pp=&p;
(*reinterpret_cast<Base * const *>(pp))->f(); // outputs 1 with BC++ or GCC, but it is UNDEFINED!
return 0;
}
Note that, replacing reinterpret_cast by static_cast leads to a compilation error on both examples.
invalid static_cast from type `Derived* const*' to type `Base* const*'
Last edited by SuperKoko; July 15th, 2005 at 07:38 AM.
-
July 15th, 2005, 09:06 AM
#6
Re: Casting - Is this allowed
"should be able to" doesn't necessarily mean "can".
Can you give me an example (without any casts at all and assuming it is legal) where converting from a Derived * const * to a Base * const * can lead to doing something illegal.
(Am I a const short? Does it need to be a Derived * const * const to Base * const * const?)
Code:
class Animal
{
public:
virtual void makeNoise() const = 0;
virtual ~Animal() {}
};
class Dog : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "woof woof!" << std::endl;
}
};
class Cat : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "miaow!" << std::endl;
}
};
int main()
{
Cat c;
Cat * const pc = &c;
Cat * const * ppc = &pc;
Dog d;
Dog * const pd = &d;
Dog * const * ppd = &pd;
Animal * const pa = pc; // legal. Can cast an Cat * const to Animal * const
Animal * const * ppa = ppc; // probably illegal but we can always cast it :)
**ppa = *pd; // this should be legal.
(*ppc)->makeNoise();
}
and your cat will probably go "woof woof!".
Last edited by NMTop40; July 15th, 2005 at 09:18 AM.
-
July 15th, 2005, 09:19 AM
#7
Re: Casting - Is this allowed
I did a reinterpret_cast but my cat is going miaow! (like it should). And the assign below the "illegal" one is a slice anyway (which is meaningless. If I disabled copying on Animal it would fail to compile that line).
And I can't reassign *ppa because that's where the const is.
So basically I can't see any way I could make pointers to cats go woof woof!. Therefore it should be a safe and legal action.
Last edited by NMTop40; July 15th, 2005 at 09:26 AM.
-
July 15th, 2005, 09:42 AM
#8
Re: Casting - Is this allowed
Code:
Base* pBase=new Base();
Derived* pDerived=new Derived();
Base** ppBase=&pBase;
Derived** ppDerived=&pDerived;
Base** ppOtherBase=(Base**)ppDerived;
Base* pOtherBase=*Base;
Now, where does ppOtherBase point? It can only point to ppDerived. It can't preserve the shift during the casting SuperKoko was talking about. And where would pOtherBase point? No way other then to the first byte of the object of type Derived as pDerived does. So, this requires that cast from Derived pointer to Base pointer doesn't change pointer. If it's not so, provided example leads to error.
"Programs must be written for people to read, and only incidentally for machines to execute."
-
July 15th, 2005, 09:55 AM
#9
Re: Casting - Is this allowed
wiithout the const I put in I can easily get my cat to go woof woof!
Code:
class Animal
{
public:
virtual void makeNoise() const = 0;
virtual ~Animal() {}
};
class Dog : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "woof woof!" << std::endl;
}
};
class Cat : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "miaow!" << std::endl;
}
};
int main()
{
Cat c;
Cat * pc = &c;
Cat ** ppc = &pc;
Dog d;
Dog * pd = &d;
Dog ** ppd = &pd;
Animal * pa = pc; // legal. Can cast an Cat * const to Animal * const
Animal ** ppa = reinterpret_cast< Animal **> (ppc);
*ppa = pd; // this should be legal.
pc->makeNoise();
}
Just tested that and my cat did indeed go woof! woof! (showing why the cast is a bad one here).
-
July 16th, 2005, 04:40 AM
#10
Re: Casting - Is this allowed
Just tested that and my cat did indeed go woof! woof! (showing why the cast is a bad one here).
Not really -- it just seems that way on first glance. The line just before calling makeNoise() reset Animal pointer from cat to dog. So the cat did NOT go "woof "
-
July 16th, 2005, 05:01 AM
#11
Re: Casting - Is this allowed
 Originally Posted by stober
Not really -- it just seems that way on first glance. The line just before calling makeNoise() reset Animal pointer from cat to dog. So the cat did NOT go "woof " 
That's the point. Allowing Cat** to Animal** cast allowed to create pointer of type Cat* to object of type Dog, which is illegal...
"Programs must be written for people to read, and only incidentally for machines to execute."
-
July 16th, 2005, 08:39 AM
#12
Re: Casting - Is this allowed
 Originally Posted by RoboTact
That's the point. Allowing Cat** to Animal** cast allowed to create pointer of type Cat* to object of type Dog, which is illegal...
casting gets even worse than that! Try typcasting to an object that is not derived from Animal, as in this example. I think the gest of this thread is that the language allows typecasting because it assumes the programmer knows what he/she is doing. And in the example below, he clearly doesn't.
Code:
class Animal
{
public:
virtual void makeNoise() const = 0;
virtual ~Animal() {}
};
class Dog : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "woof woof!" << std::endl;
}
};
class Cat : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "miaow!" << std::endl;
}
};
class Insect
{
public:
virtual void makeNoise() const = 0;
virtual ~Insect() {}
};
class BumbleBee : public Insect
{
public:
virtual void makeNoise() const
{
std::cout << "bzzzzzz!" << std::endl;
}
};
int main()
{
Cat c;
Dog d;
Cat * pc = &c;
Cat ** ppc = &pc;
Dog * pd = &d;
Dog ** ppd = &pd;
Animal * pa = pc; // legal. Can cast an Cat * const to Animal * const
Animal ** ppa = reinterpret_cast< Animal **> (ppc);
*ppa = pd; // this should be legal.
pc->makeNoise();
BumbleBee b;
Insect* pInsect = &b;
ppa = reinterpret_cast< Animal **> (pInsect);
(*ppa)->makeNoise();
return 0;
}
class Animal
{
public:
virtual void makeNoise() const = 0;
virtual ~Animal() {}
};
class Dog : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "woof woof!" << std::endl;
}
};
class Cat : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "miaow!" << std::endl;
}
};
class Insect
{
public:
virtual void makeNoise() const = 0;
virtual ~Insect() {}
};
class BumbleBee : public Insect
{
public:
virtual void makeNoise() const
{
std::cout << "bzzzzzz!" << std::endl;
}
};
int main()
{
Cat c;
Dog d;
Cat * pc = &c;
Cat ** ppc = &pc;
Dog * pd = &d;
Dog ** ppd = &pd;
Animal * pa = pc; // legal. Can cast an Cat * const to Animal * const
Animal ** ppa = reinterpret_cast< Animal **> (ppc);
*ppa = pd; // this should be legal.
pc->makeNoise();
BumbleBee b;
Insect* pInsect = &b;
ppa = reinterpret_cast< Animal **> (pInsect);
(*ppa)->makeNoise(); // CRASH!
return 0;
}
Last edited by stober; July 16th, 2005 at 08:41 AM.
-
July 16th, 2005, 08:47 AM
#13
Re: Casting - Is this allowed
Converting from Insect* to Animal** doesn't make any sense, even in the context of this thread. They are independant classes, and even worse: you convert different levels of indirection.
"Programs must be written for people to read, and only incidentally for machines to execute."
-
July 16th, 2005, 09:00 AM
#14
Re: Casting - Is this allowed
 Originally Posted by RoboTact
Converting from Insect* to Animal** doesn't make any sense, even in the context of this thread. They are independant classes, and even worse: you convert different levels of indirection.
Of course! that's why my program crashed. This works.
Code:
ppa = reinterpret_cast< Animal **> (&pInsect);
(*ppa)->makeNoise();
and the output
Code:
woof woof!
bzzzzzz!
Press any key to continue
And I think the reason it works is because of virtual table -- replacing one function pointer with another function pointer. You can even rename the makeNoise() function in Insert and BumbleBee and the results will still be the same. It seems to be the order of the pure virtual functions, not the function names themselves.
Last edited by stober; July 16th, 2005 at 09:08 AM.
-
July 16th, 2005, 09:37 AM
#15
Re: Casting - Is this allowed
Anyway, Insect isn't derived from Animal.
"Programs must be written for people to read, and only incidentally for machines to execute."
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|