Re: cast operation

Michael J. Saletnik (michael.saletnik@tfn.com)
Fri, 23 Jul 1999 14:21:23 +0000


Onuchin Valeriy wrote:

> Dear C++ experts, I have few questions about cast operation.

Okay, a quick test on a compiler would have answered many of these
questions. However, let us press on, since we all know compilers are
never the answer, only the source of more questions.

> class A
> {
> private:
> Object* theGoal;
> public:
> Object* GetTheGoal() const { return theGoal; }
> ...
> };

Note that if you ever wish for there to be C::GetTheGoal() you should
declare this method virtual, otherwise it "hides" and you get different
behavior. And that's important.

> class B; // some other class
> class C: public A, public B // multiple inherited
> {
> ...
> };
> B* d = new C();
> Object* goal1 = ((A*)d)->GetTheGoal(); // cast B to A
> Object* goal2 = ((C*)d)->GetTheGoal(); // cast B to C

Okay, that's the source code. Let's assume that you do *not* declare a
method C::GetTheGoal. In that case, both operations will call
A::GetTheGoal (there's no other option). Both will compile fine.

BUT ...

To clearly understand what's going on, stop using C-style casts. Use C++
casts. You will find that you would be forced to use:

(reinterpret_cast<A*>(d))->GetTheGoal();
(static_cast<C*>(d))->GetTheGoal();

to compile. And you would clearly realize that you are doing a cast that
means more than just syntax to the compiler. And that you are simply
lucky because you do not have any virtual functions. A reintepreted cast
is a dangerous thing.

Let's throw virtuality into the mix.
Change A::GetTheGoal() to be virtual, and declare and define
C::GetTheGoal() to be different (so we can tell which is called).

Now, when the compiler tries to do the reinterpret_cast<A*>(d) (or for
that matter, the C-style cast), it seg faults because B and A are not
related. It can't do it the runtime reinterpretation. It's illegal.

Here's the very interesting example!
virtual A::GetTheGoal exists.
C::GetTheGoal also exists.
C *d = new C;
then:
d->GetTheGoal();
and
(static_cast<A*>(d))->GetTheGoal();
will *both* call C::GetTheGoal() !!!
To see a

But if you don't declare A::GetTheGoal as virtual, then:
d->GetTheGoal() --> C::GetTheGoal();
(cast to A*)(d)->GetTheGoal() --> A::GetTheGoal();

Different behavior.
Confused yet?
Let's sum up:

> 0. what cast from above is correct?

You should do neither. Casting B to A is illegal - they are not related.
Casting B to C is allowed, but should never be required (C inherits B
and thus can be used anywhere B is allowed) because not only is that the
point of inheritence, but can you guarantee that if someone passes you a
B* it's pointing to C? What if I declare D inheriting B and pass you a
D*?.

Casting C down to B is allowed, but depending on the data passing type,
you can "slice" off all the specific information to C and if you tried
to cast back from B to C would get garbage.

To quote Stroustrup:
"If you feel tempted to use an explicit type conversion, take the time
to consider if it is *really* necessary. In C++, explicit type
conversion is unnecessary in most cases when C needs it and also in many
cases in which earlier versions of C++ needed it."

Oh, and the difference of static_cast<>() and reinterpret_cast<>() is
the relation of types before and after the cast. A static cast would be
something like enum to int; a reinterpreted cast like integer to
pointer.

> 1. if there are rules to avoid segmentation violation errors?

Don't write illegal casts.
:-)

> 2. what is difference between declarations
> class C: public A, public B
> and
> class C: public B, public A

In QT or in C++ generally? In QT, it makes a difference to 'moc'. In
C++ it mandates the order in which base classes are constructed (unless
overridden by an initializer in a constructor). I think. Double-check. I
avoid multiple inheritance wherever possible.

> 3. if cast operation is platform/compiler dependent ?

The C++ casts are a part of the C++ specification. What is p/c dependent
is the way they are performed, especially when it comes to base and
derived classes and vtbl lookups.

-Michael

--
Michael J. Saletnik
Software Engineer, AutEx, TFS Boston