Newbie inheritance question: Any way to go from base to derived?

Discussion in 'C++' started by Ex_Ottoyuhr, Jul 20, 2004.

  1. Ex_Ottoyuhr

    Ex_Ottoyuhr Guest

    Given a situation in which someone has, say:

    class Foo { ... }
    class Bar: public Foo { ... }
    class Baz: public Foo { ... }
    class Bindi: public Baz { ... }

    and later in the code:

    Bindi somebody;
    Foo* aPtr = &somebody;
    std::vector<Foo*> somePtrs;
    somePtrs.push_back(aPtr);

    (and manipulations of them)

    is there any legal way to get from somePtrs to somebody? That is, is
    it possible, in this case, to take a pointer to a base class, say Foo,
    and convert it to a pointer to a derived class several levels below,
    say Bindi, and get meaningful data (assuming the Foo pointer really
    does point to a Bindi)?

    I'm working on a program wherein the only means to get a pointer to an
    object of any member of a particular inheritance hierarchy is to get a
    pointer to the root of that hierarchy out of a linked list.
    Unfortunately, as I've just discovered (and should have realized much
    sooner), several child classes have functions that would be completely
    pointless outside of themselves (ruling out adding them to the base
    class), but are vital to the child class' operation. So far, I've
    tried all sorts of dangerous ideas -- static casting, dynamic casting,
    even reinterpret-cast -- but the former two were illegal and the last
    turned up gibberish. Is there any way to get at these member functions
    without rewriting the code to use pointers to the child classes? I've
    been working on this thing for _quite_ some time, and it would involve
    rewrites of a substantial codebase...

    I'm running Visual Studio .NET 2003, if it helps.
     
    Ex_Ottoyuhr, Jul 20, 2004
    #1
    1. Advertising

  2. * Ex_Ottoyuhr:
    > Given a situation in which someone has, say:
    >
    > class Foo { ... }
    > class Bar: public Foo { ... }
    > class Baz: public Foo { ... }
    > class Bindi: public Baz { ... }
    >
    > and later in the code:
    >
    > Bindi somebody;
    > Foo* aPtr = &somebody;
    > std::vector<Foo*> somePtrs;
    > somePtrs.push_back(aPtr);
    >
    > (and manipulations of them)
    >
    > is there any legal way to get from somePtrs to somebody? That is, is
    > it possible, in this case, to take a pointer to a base class, say Foo,
    > and convert it to a pointer to a derived class several levels below,
    > say Bindi, and get meaningful data (assuming the Foo pointer really
    > does point to a Bindi)?
    >
    > I'm working on a program wherein the only means to get a pointer to an
    > object of any member of a particular inheritance hierarchy is to get a
    > pointer to the root of that hierarchy out of a linked list.
    > Unfortunately, as I've just discovered (and should have realized much
    > sooner), several child classes have functions that would be completely
    > pointless outside of themselves (ruling out adding them to the base
    > class), but are vital to the child class' operation. So far, I've
    > tried all sorts of dangerous ideas -- static casting, dynamic casting,
    > even reinterpret-cast -- but the former two were illegal and the last
    > turned up gibberish. Is there any way to get at these member functions
    > without rewriting the code to use pointers to the child classes? I've
    > been working on this thing for _quite_ some time, and it would involve
    > rewrites of a substantial codebase...


    If static_cast was illegal then the example code does not match your
    real code: the types are not related.

    Try to post an example that compiles and illustrates the problem, or
    alternatively one that shows where and how the compiler chokes.

    General comment: even if the technical issues are solved there is still
    one big _design_ issue, namely how to avoid downcasting. This issue has
    a number of different solutions, but the most general is that virtual
    functions were invented precisely in order to do that safely. Which
    solutions are applicable cannot be stated without a more real example.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jul 20, 2004
    #2
    1. Advertising

  3. Ex_Ottoyuhr

    Greg Schmidt Guest

    On 20 Jul 2004 12:58:08 -0700, Ex_Ottoyuhr wrote:

    > Given a situation in which someone has, say:
    >
    > class Foo { ... }
    > class Bar: public Foo { ... }
    > class Baz: public Foo { ... }
    > class Bindi: public Baz { ... }
    >
    > and later in the code:
    >
    > Bindi somebody;
    > Foo* aPtr = &somebody;
    > std::vector<Foo*> somePtrs;
    > somePtrs.push_back(aPtr);
    >
    > (and manipulations of them)
    >
    > is there any legal way to get from somePtrs to somebody? That is, is
    > it possible, in this case, to take a pointer to a base class, say Foo,
    > and convert it to a pointer to a derived class several levels below,
    > say Bindi, and get meaningful data (assuming the Foo pointer really
    > does point to a Bindi)?


    Sounds like a clear cut case for dynamic_cast. If it doesn't work for you,
    then you're doing it wrong. :) Post an actual (minimal) code example that
    demonstrates the problem.

    It shouldn't be considered "dangerous", this is what it is for. (Of
    course, you need to catch the bad_cast exception that happens if you
    dynamic_cast to a class that doesn't match the object's actual class.)

    --
    Greg Schmidt
    Trawna Publications http://www.trawna.com/
     
    Greg Schmidt, Jul 20, 2004
    #3
  4. Ex_Ottoyuhr

    Ex_Ottoyuhr Guest

    (Alf P. Steinbach) wrote in message news:<>...
    > * Ex_Ottoyuhr:

    <Downcasting problem snipped... >

    > If static_cast was illegal then the example code does not match your
    > real code: the types are not related.
    >
    > Try to post an example that compiles and illustrates the problem, or
    > alternatively one that shows where and how the compiler chokes.
    >
    > General comment: even if the technical issues are solved there is still
    > one big _design_ issue, namely how to avoid downcasting. This issue has
    > a number of different solutions, but the most general is that virtual
    > functions were invented precisely in order to do that safely. Which
    > solutions are applicable cannot be stated without a more real example.


    OK; sorry about not posting an example sooner. I've managed to get to
    the point where the dynamic_cast is syntatically legal, thanks to your
    and Mr. Schmidt's advice, which I greatly appreciate. (Part of my
    problem was that I was trying to dynamic_cast a pointer returned from
    a function, which I don't think is legal...)

    Unfortunately, the code below -- an example of the revised, slightly
    smaller problem -- throws an exception in dgbheap.c, at least under
    Visual Studio, and I get the feeling its behavior under, say, g++, is
    probably not much prettier.

    /*
    * File: Dynacast-prob -- An example of my dynamic-casting woes.
    */
    #include <iostream>
    #include <string>
    #include <list>
    using std::cout;
    using std::string;
    using std::list;

    // The classes.

    class Fellow {
    public:
    string myName;
    virtual void doThing() {
    cout << "Doing a thing in Fellow.\n";
    return;
    }
    };

    class Tom : public Fellow {
    public:
    string aString;
    void doThing() {
    cout << "Doing a thing in Tom.\n";
    }
    };

    class Bob : public Fellow {
    public:
    void sayThing( string aStr ) {
    cout << aStr << std::endl;
    return;
    }
    virtual void doThing() {
    cout << "Doing a thing in Bob.\n";
    }
    };

    class Bill : public Bob {
    public:
    // Assume this is something logical only for Bills, and would be dead
    weight
    // anywhere else.
    string concatStrings ( string left, string right ) {
    return ( left.append(right.begin(), right.end()) );
    }

    void doThing() {
    cout << "Doing a thing in Bill.\n";
    }
    };

    int main ( int argc, char** argv ) {
    Bill aBill;
    aBill.myName = "Bill";
    Tom aTom;
    aTom.myName = "Tom";
    Bob aBob;
    aBob.myName = "Bob";
    std::list<Fellow*> aList;
    aList.push_back(&aBob);
    aList.push_back(&aBill);
    aList.push_back(&aTom);

    Fellow* aFellowPtr = NULL;

    // Now, try to locate Bill and exit.
    for ( std::list<Fellow*>::iterator i = aList.begin(); i !=
    aList.end(); i++ ) {
    if ( (*i)->myName == "Bill" ) {
    aFellowPtr = *i;
    break;
    }
    }

    // If aFellowPtr is not NULL, we found a Fellow named Bill. So, how
    do I go from
    // knowing that my Fellow* points to somebody named Bill to being
    able to call
    // aFellowPtr->concatStrings()?
    if ( aFellowPtr != NULL ) {
    cout << "Found the Fellow. Now what?" << std::endl;
    // This throws an exception.
    Bill* aBill = dynamic_cast<Bill*>(aFellowPtr);
    }

    }
     
    Ex_Ottoyuhr, Jul 21, 2004
    #4
  5. * Ex_Ottoyuhr:
    > (Alf P. Steinbach) wrote in message news:<>...
    > > * Ex_Ottoyuhr:

    > <Downcasting problem snipped... >
    >
    > > If static_cast was illegal then the example code does not match your
    > > real code: the types are not related.
    > >
    > > Try to post an example that compiles and illustrates the problem, or
    > > alternatively one that shows where and how the compiler chokes.
    > >
    > > General comment: even if the technical issues are solved there is still
    > > one big _design_ issue, namely how to avoid downcasting. This issue has
    > > a number of different solutions, but the most general is that virtual
    > > functions were invented precisely in order to do that safely. Which
    > > solutions are applicable cannot be stated without a more real example.

    >
    > OK; sorry about not posting an example sooner. I've managed to get to
    > the point where the dynamic_cast is syntatically legal, thanks to your
    > and Mr. Schmidt's advice, which I greatly appreciate. (Part of my
    > problem was that I was trying to dynamic_cast a pointer returned from
    > a function, which I don't think is legal...)
    >
    > Unfortunately, the code below -- an example of the revised, slightly
    > smaller problem -- throws an exception in dgbheap.c, at least under
    > Visual Studio, and I get the feeling its behavior under, say, g++, is
    > probably not much prettier.
    >
    > /*
    > * File: Dynacast-prob -- An example of my dynamic-casting woes.
    > */
    > #include <iostream>
    > #include <string>
    > #include <list>
    > using std::cout;
    > using std::string;
    > using std::list;
    >
    > // The classes.
    >
    > class Fellow {
    > public:
    > string myName;
    > virtual void doThing() {
    > cout << "Doing a thing in Fellow.\n";
    > return;
    > }
    > };
    >
    > class Tom : public Fellow {
    > public:
    > string aString;
    > void doThing() {
    > cout << "Doing a thing in Tom.\n";
    > }
    > };
    >
    > class Bob : public Fellow {
    > public:
    > void sayThing( string aStr ) {
    > cout << aStr << std::endl;
    > return;
    > }
    > virtual void doThing() {
    > cout << "Doing a thing in Bob.\n";
    > }
    > };
    >
    > class Bill : public Bob {
    > public:
    > // Assume this is something logical only for Bills, and would be dead
    > weight
    > // anywhere else.
    > string concatStrings ( string left, string right ) {
    > return ( left.append(right.begin(), right.end()) );
    > }
    >
    > void doThing() {
    > cout << "Doing a thing in Bill.\n";
    > }
    > };
    >
    > int main ( int argc, char** argv ) {
    > Bill aBill;
    > aBill.myName = "Bill";
    > Tom aTom;
    > aTom.myName = "Tom";
    > Bob aBob;
    > aBob.myName = "Bob";
    > std::list<Fellow*> aList;
    > aList.push_back(&aBob);
    > aList.push_back(&aBill);
    > aList.push_back(&aTom);
    >
    > Fellow* aFellowPtr = NULL;
    >
    > // Now, try to locate Bill and exit.
    > for ( std::list<Fellow*>::iterator i = aList.begin(); i !=
    > aList.end(); i++ ) {
    > if ( (*i)->myName == "Bill" ) {
    > aFellowPtr = *i;
    > break;
    > }
    > }
    >
    > // If aFellowPtr is not NULL, we found a Fellow named Bill. So, how
    > do I go from
    > // knowing that my Fellow* points to somebody named Bill to being
    > able to call
    > // aFellowPtr->concatStrings()?
    > if ( aFellowPtr != NULL ) {
    > cout << "Found the Fellow. Now what?" << std::endl;
    > // This throws an exception.
    > Bill* aBill = dynamic_cast<Bill*>(aFellowPtr);
    > }
    >
    > }


    With fix of line breaks in comments this compiles and runs fine under
    MSVC 7.1.

    As an experiment I turned off RTTI support and then got the compilation
    warning

    warning C4541: 'dynamic_cast' used on polymorphic type 'Fellow' with
    /GR-; unpredictable behavior may result

    and then when running the program the exception

    Access violation - no RTTI data!

    So it might be that you're compiling with RTTI support turned off, in
    other words, in a non-comforming mode (unfortunately MSVC default).

    That said, you can and probably should avoid the dynamic_cast by using
    the Visitor pattern.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jul 21, 2004
    #5
  6. Ex_Ottoyuhr

    Ex_Ottoyuhr Guest

    (Alf P. Steinbach) wrote in message news:<>...
    > * Ex_Ottoyuhr:
    > > (Alf P. Steinbach) wrote in message news:<>...
    > > > * Ex_Ottoyuhr:

    > > <Downcasting problem snipped... >

    >
    > With fix of line breaks in comments


    Sorry about that -- I'm posting through Google Groups, it's not always
    good about respecting those sorts of things...

    > this compiles and runs fine under
    > MSVC 7.1.
    >
    > As an experiment I turned off RTTI support and then got the compilation
    > warning
    >
    > warning C4541: 'dynamic_cast' used on polymorphic type 'Fellow' with
    > /GR-; unpredictable behavior may result
    >
    > and then when running the program the exception
    >
    > Access violation - no RTTI data!
    >
    > So it might be that you're compiling with RTTI support turned off, in
    > other words, in a non-comforming mode (unfortunately MSVC default).


    Yes, I had it turned off. Oops... Well, I'm glad a solution exists,
    especially one that simple. Stupid non-compliant Microsoft compiler...
    :)

    > That said, you can and probably should avoid the dynamic_cast by using
    > the Visitor pattern.


    Pardon my newbiness... Visitor pattern? <Googles>

    OK, I _think_ I get it. Just to be sure, I'll paraphrase it here --
    correct me if I'm wrong. When using the visitor pattern, the parent
    ABC of a hierarchy of classes has a pure-virtual, generic function to
    accept a "Visitor" functionoid. This Visitor has a different member
    function for some subset of all classes in the ABC. The behavior of
    the Visit function for a given class is to invoke the appropriate
    member function of the Visitor for that class, which then does
    whatever behavior was desired.

    Sounds fairly straightforward. Most examples I saw in looking around
    online had member functions like "Visitor::actOnThisA()",
    "Visitor::actOnThisB()", etc.; is there any reason not to overload
    operator() instead?

    Thanks a lot for the advice; this makes things much better.
     
    Ex_Ottoyuhr, Jul 22, 2004
    #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. qazmlp
    Replies:
    1
    Views:
    590
    qazmlp
    Apr 10, 2005
  2. Replies:
    4
    Views:
    436
    Alf P. Steinbach
    May 23, 2007
  3. Replies:
    1
    Views:
    419
    myork
    May 23, 2007
  4. Replies:
    1
    Views:
    405
    Victor Bazarov
    May 23, 2007
  5. Replies:
    2
    Views:
    745
Loading...

Share This Page