[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: [OT] C++ question re. dyn. mem.

>>>>> "MJM" == MJM  <linux-support@earthlink.net> writes:

    MJM> They do. My app would be broken from the start if I could not rely
    MJM> on this capability.  This style of type conversion is covered in
    MJM> elementary C++ books by Bjarne.  It's not unusual.  You must be
    MJM> aware of what you are doing when you do a type conversion.
    MJM> Portability is a concern.  I am limiting my app to Intel 32 bit
    MJM> Linux.  Screw everything else.

That you do not treasure portability across CPUs and compilers do not mean
others don't.

    MJM> By whom? Your example is nowhere to be found in my C++ books by
    MJM> Bjarne.  So you are saying that Bjarne promotes bad style in his
    MJM> books? Why not tell him:
    MJM> http://www.research.att.com/~bs/homepage.html

You must be reading pre-standard C++ books.  Bjarne's "The C++ Programming
Language, 3rd Edition" clearly stated that the () "C-style" casting syntax
should be avoided:

  This C-style cast is far more dangerous than the named conversion
  operators because the notation is harder to spot in a large program and
  the kind of conversion intended by the programmer is not
  explicit. [Section 6.2.7.]

In his "The Design and Evolution of C++", Bjarne explained that he even
wanted to strike the C-style cast out of the C++ standard except that all C
programs would become not compilable.

    MJM> Besides, reinterpret_cast is probably a template function doing
    MJM> this:

    MJM> return ((T) x); // type conversion using cast

Definitely not.  The objection of (T)x is not just its syntax, but also its
unclear behaviour.  Consider the following:

        #include <iostream>
        using namespace std;

        class B {
                virtual ~B() {}
        class B1 {
                virtual ~B1() {}
        class B2 {
                virtual ~B2() {}
        class D: public B1, public B2 {};
        int main() {
                D d;
                B *pb;
                B2 *pb1;
                pb1 = (B2 *)&d;
                cout << pb1 << endl;
                pb1 = reinterpret_cast<B2 *>(&d);
                cout << pb1 << endl;
                pb1 = static_cast<B2 *>(&d);
                cout << pb1 << endl;
                pb = (B *)&d;
                cout << pb << endl;

When executed from my computer (Debian sid, gcc 3.3), it gives:

        0xbffffb04  # C-style cast to B2*
        0xbffffb00  # reinterpret_cast to B2*
        0xbffffb04  # static_cast to B2*
        0xbffffb00  # C-style cast to B*

In other words, the behaviour of a C-style cast depends on whether the
casting of the pointer is an "up-cast"/"down-cast" or not; if it is (in this
case up from D to B2) then the cast will be a static cast, doing pointer
adjustment (so that the sub-object of type B2 is found within the object of
type D); and if it is not then the cast will be a reinterpret cast, doing no
pointer adjustment.  It is generally agreed that such "compiler
intelligence" is in general no good, since the programmer probably only
expect one of the two possibility will happen, and the result is sometimes
what the programmer expects and sometimes not.  If the programmer expects a
static cast he should write static_cast<B2 *>(&d) so that the compiler will
emit an error if the classes are actually of two different hierarchy.  If
the programmer expects a dynamic cast he should write dynamic_cast<B *>(&d)
so that you understand you are doing something that the compiler has no
control of, and dereferencing the result will probably be a programming


Reply to: