Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Newbie inheritance question: Any way to go from base to derived?

Reply
Thread Tools

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

 
 
Ex_Ottoyuhr
Guest
Posts: n/a
 
      07-20-2004
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.
 
Reply With Quote
 
 
 
 
Alf P. Steinbach
Guest
Posts: n/a
 
      07-20-2004
* 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?
 
Reply With Quote
 
 
 
 
Greg Schmidt
Guest
Posts: n/a
 
      07-20-2004
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 http://www.velocityreviews.com/forums/(E-Mail Removed)
Trawna Publications http://www.trawna.com/
 
Reply With Quote
 
Ex_Ottoyuhr
Guest
Posts: n/a
 
      07-21-2004
(E-Mail Removed) (Alf P. Steinbach) wrote in message news:<(E-Mail Removed)>...
> * 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);
}

}
 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      07-21-2004
* Ex_Ottoyuhr:
> (E-Mail Removed) (Alf P. Steinbach) wrote in message news:<(E-Mail Removed)>...
> > * 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?
 
Reply With Quote
 
Ex_Ottoyuhr
Guest
Posts: n/a
 
      07-22-2004
(E-Mail Removed) (Alf P. Steinbach) wrote in message news:<(E-Mail Removed)>...
> * Ex_Ottoyuhr:
> > (E-Mail Removed) (Alf P. Steinbach) wrote in message news:<(E-Mail Removed)>...
> > > * 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.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Any way to detect the absense of virtual destructor in base class? Qi C++ 16 11-26-2011 12:16 PM
501 PIX "deny any any" "allow any any" Any Anybody? Networking Student Cisco 4 11-16-2006 10:40 PM
any way of modify base directory at runtime? Bruno Java 2 10-06-2006 04:57 PM
mul. inheritance & overloading operator new/delete solved by virtual base inheritance? cppsks C++ 0 10-27-2004 07:49 PM
Virtual function 'BasicMidReader::~BasicMidReader()' conflicts with base class 'base 'TMemoryStream' tomek C++ 2 12-01-2003 06:31 AM



Advertisments