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

E

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...

I'm running Visual Studio .NET 2003, if it helps.
 
A

Alf P. Steinbach

* 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.
 
G

Greg Schmidt

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.)
 
E

Ex_Ottoyuhr

* Ex_Ottoyuhr:
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);
}

}
 
A

Alf P. Steinbach

* Ex_Ottoyuhr:
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.
 
E

Ex_Ottoyuhr

* Ex_Ottoyuhr:

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top