accessing subclass members via a base pointer?

M

Me

I need to be able to acces non-virtual members of sublcasses via a
base class pointer...and without the need for an explicit type cast.
I thought a pure virtual getPtr() that acts as a type cast would solve
the problem, but it appears not to.

I need this functionality to make object serialization a reality. Having
to explicitly cast each deserialized object to its original type defeats
the purpose of my serializing the blasted things in the first place. The
serialization functions all work at this point but the deserialized object,
accessible via a base class pointer, must allow for an "implicit" cast to
the correct subclass. See below...


#include <iostream>
#include <iomanip>

struct base {
virtual base* getPtr()=0;
};
struct subclass: public base {
subclass* getPtr() { return this; }
const char* isA() { return "subclass"; }
};

using namespace std;
int main(int argc, char** argv) {
base* p=new subclass();

// $ g++ test.cc # using gcc 4.1.2 and glibc 2.5.18
// test.cc: In function 'int main()':
// test.cc:19: error: 'struct base' has no member named 'classname'
cout << p->getPtr()->isA() << endl;

return 0;
};

I would EXPECT p->getPtr() to do a correct type cast of the pointer so
that ->isA() is called with a pointer of the correct type, but that's not
happening...WTF!?
 
T

Tim H

I need to be able to acces non-virtual members of sublcasses via a
base class pointer...and without the need for an explicit type cast.
I thought a pure virtual getPtr() that acts as a type cast would solve
the problem, but it appears not to.

I need this functionality to make object serialization a reality.

I think you're stuck. Why not make a virtual SerializeIt() method
instead?

See below for details
#include <iostream>
#include <iomanip>

struct base {
virtual base* getPtr()=0;};

struct subclass: public base {
subclass* getPtr() { return this; }
const char* isA() { return "subclass"; }

};

getPtr() is not virtual so it is statically bound based on the type,
which is known at compile time.
using namespace std;
int main(int argc, char** argv) {
base* p=new subclass();


p has type "pointer to base"
cout << p->getPtr()->isA() << endl;

Since p is a "base" and getPtr() is not virtual, I would expect the
compiler to generate a call to base::getPtr(). Since base doesn't
have an isA() method in this code, I'd expect it to fail. Does it
actually compile, or does your real code have a base::isA() method?
 
J

Just me

I think you're stuck. Why not make a virtual SerializeIt() method
instead?

Serialization is not a problem and that part works. The problem is
that deserialization is pretty much useless if I still have to do
an explicit cast from a base pointer type back to the correct
subclass in the heirarchy upon object reconstruction.
getPtr() is not virtual so it is statically bound based on the type,
which is known at compile time.

If you look closely you will see that getPtr() is declared virtual in the
base class and the correct sublcass::getPtr() is being called. I verified
this by inserting an exit(0) in subclass::getPtr().

Since p is a "base" and getPtr() is not virtual, I would expect the
compiler to generate a call to base::getPtr(). Since base doesn't have
an isA() method in this code, I'd expect it to fail. Does it actually
compile, or does your real code have a base::isA() method?

see above and note that the compiler error was in the OP...the virtual
nature of getPtr() has been verified but the compiler wont recognize the
subclass* return value of getPtr() to allow access to subclass::isA().

What I'm seeing is weird because the epxression p->getPtr() should return
a subclass* and that return type should be compatible with subclass::isA().
 
G

Guest

Serialization is not a problem and that part works. The problem is
that deserialization is pretty much useless if I still have to do
an explicit cast from a base pointer type back to the correct
subclass in the heirarchy upon object reconstruction.


If you look closely you will see that getPtr() is declared virtual in the
base class and the correct sublcass::getPtr() is being called. I verified
this by inserting an exit(0) in subclass::getPtr().

What Tim meant was that you are not allowed to change the return type of
a virtual function. However, returning a type that is a subtype of the
original function's is allowed, but that does not help you.
see above and note that the compiler error was in the OP...the virtual
nature of getPtr() has been verified but the compiler wont recognize the
subclass* return value of getPtr() to allow access to subclass::isA().

What I'm seeing is weird because the epxression p->getPtr() should return
a subclass* and that return type should be compatible with subclass::isA().

No, the pointer p is of of type base*, that means that the function
called is base's getPtr() (but the code that is executed is one in
subclass) which means that the type of the pointer that is returned from
getPtr() have the type declared in base's getPtr(), namely base*.

With a pointer to base you can only call base's functions. Though if
they are virtual and the actual type of the object pointed to is a
derived type then the implementation in the derived type will be used.
But it is still the base's function that was called. What you can do is
make isA() a virtual function in base, and use the value returned to
dynamic_cast() the pointer to the correct subtype.
 
M

Me

No, the pointer p is of of type base*, that means that the function
called is base's getPtr() (but the code that is executed is one in
subclass) which means that the type of the pointer that is returned from
getPtr() have the type declared in base's getPtr(), namely base*.

With a pointer to base you can only call base's functions. Though if
they are virtual and the actual type of the object pointed to is a
derived type then the implementation in the derived type will be used.
But it is still the base's function that was called. What you can do is
make isA() a virtual function in base, and use the value returned to
dynamic_cast() the pointer to the correct subtype.

then it is still an exercise in futility because the whole purpose was to
avoid having to do an explicit cast through some switch() statement...so
unless dynamic_cast<> takes runtime computed values as its template
argument, then all is for not...

regarding changing the return type of a virtual: the compiler should
warn me of this if it is not allowed...The assumption was that it is
allowed since there was no compiler error there and the correct
getPtr() virtual was being executed.


thanks.
 
G

Guest

then it is still an exercise in futility because the whole purpose was to
avoid having to do an explicit cast through some switch() statement...so
unless dynamic_cast<> takes runtime computed values as its template
argument, then all is for not...

No, it does not.
regarding changing the return type of a virtual: the compiler should
warn me of this if it is not allowed...The assumption was that it is
allowed since there was no compiler error there and the correct
getPtr() virtual was being executed.

As I said, it is not allowed. But in your case you did not actually
change the return type as much as being a little more specific about it.
The getPtr in base returned a pointer to base, while the getPtr() in
subclass returned a pointer to a subclass. And since a pointer to a
subclass is (or rather is convertible to) a pointer to base the return
type was not changed. And you were correct in assuming the correct
getPtr() was executed.
 
J

James Kanze

[...]
What Tim meant was that you are not allowed to change the
return type of a virtual function. However, returning a type
that is a subtype of the original function's is allowed, but
that does not help you.

What you meant is that returning a pointer or a reference to a
derived type when the function in the base class returns a
pointer or reference to a base is legal. It's called co-variant
return. But of course, the type of the pointer which gets used
at the call site is determined statically. If the static type
is base*, it can only be used to call functions declared in
base. Regardless of what the base* really points to.
No, the pointer p is of of type base*, that means that the
function called is base's getPtr() (but the code that is
executed is one in subclass)

Only if the function is virtual. Basically, the entire call
sequence is first resolved (overloading, etc.) statically.
Then, if and only if the chosen function is declared virtually,
the compiler will invoke the function of the most derived class.
But before any such considerations, the compiler must see a
declaration of the function. Using purely static analysis.

FWIW, this is a standard behavior of a statically typed
language. It's not specific to C++: Java, C# and I believe
Ada95 and Eiffel all have the same behavior. If you want
something different, you need a fully dynamically typed
language, like Lisp or Smalltalk. Which has other
disadvantages: whatever language you choose represents a
compromise between safety and flexibility (with C++ leaning
very, very strongly to the side of safety).
which means that the type of the pointer that is returned from
getPtr() have the type declared in base's getPtr(), namely
base*.
With a pointer to base you can only call base's functions.
Though if they are virtual and the actual type of the object
pointed to is a derived type then the implementation in the
derived type will be used. But it is still the base's
function that was called.

I know what you're trying to say, but that sounds a bit
confusing, since of course, you don't actually call the function
in base---if it is pure virtual, there is no function to call.
I prefer to explain it in terms of compile time "resolution"
(given that we already have to term "overload resolution"). The
compiler must first choose which function to call, statically;
how can it know if the function is virtual until it has choosen
it?

The actual "resolution" is a fairly complex process, involving
name lookup, including ADL, then type deduction on template
parameters, and finally overload resolution. Having "resolved"
the function, the compiler then does things like access checks
(private, etc.), and only after all this does it consider
whether the function is virtual or not, and either call the
function in the statically declared type or the one in the
dynamic type.
What you can do is make isA() a virtual function in base, and
use the value returned to dynamic_cast() the pointer to the
correct subtype.

There are a number of possible solutions, but since we don't
really know the problem...
 

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

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,072
Latest member
trafficcone

Latest Threads

Top