Type "assurance" of derived classes

O

Oliver Graeser

Hi All,

I'm coming from Java to C++ and this is one of the very last problems I
have so far... In Java, if I have, say, a class SISNode that extends
NetworkNode, I can have a function that returns a NetworkNode but I can
assure the compiler that it is in fact a SISNode and therefore call the
method getStatus() that only a SISNode has. Like

SISnode s,t;
NetworkNode n;
n =t;
n.getStatus();//won't work
s= (SISNode) n;
s.getStatus(); //will work
....
....

I'm now looking for some way to do this in C++. I do agent-based network
simulations, and I want to derive all kinds of agents from a generic
network node type. This network node is supposed to store his neighbours
in a std::list<GenericNetworkNode> list. Now in the derived classes I
can obtain the neighbours, but I cannot call their methods unless they
were already declared in the GenericNetworkNode declaration.

Anybody knows how to solve this problem? A hint in the right direction
(keyword) would be more than enough....

Thanks

Oliver
 
P

Pascal J. Bourguignon

Oliver Graeser said:
Hi All,

I'm coming from Java to C++ and this is one of the very last problems
I have so far... In Java, if I have, say, a class SISNode that extends
NetworkNode, I can have a function that returns a NetworkNode but I
can assure the compiler that it is in fact a SISNode and therefore
call the method getStatus() that only a SISNode has. Like

SISnode s,t;
NetworkNode n;
n =t;
n.getStatus();//won't work
s= (SISNode) n;
s.getStatus(); //will work

No it won't.
...
...

I'm now looking for some way to do this in C++. I do agent-based
network simulations, and I want to derive all kinds of agents from a
generic network node type. This network node is supposed to store his
neighbours in a std::list<GenericNetworkNode> list. Now in the derived
classes I can obtain the neighbours, but I cannot call their methods
unless they were already declared in the GenericNetworkNode
declaration.

Anybody knows how to solve this problem? A hint in the right direction
(keyword) would be more than enough....

This is not possible, with what you wrote above and the assumed
declarations. You must realise that when you run n=t; you lose all
the information specific to a SISNode. There is no way to build it
back.

That's why I consider that C++ objects should always be allocated
dynamically and referend thru a pointer.

If you had written:

class SISNode:public NetworkNode {...};

SISNode* s=new SISNode();
NetworkNode* n=s;

then you could write:

SISNode* nAsSISNode=dynamic_cast<SISNode*>n;
if(nAsSISNode!=0){ nAsSISNode->getStatus(); }
 
E

Erik Wikström

No it won't.


This is not possible, with what you wrote above and the assumed
declarations. You must realise that when you run n=t; you lose all
the information specific to a SISNode. There is no way to build it
back.

That's why I consider that C++ objects should always be allocated
dynamically and referend thru a pointer.

If you had written:

class SISNode:public NetworkNode {...};

SISNode* s=new SISNode();
NetworkNode* n=s;

then you could write:

SISNode* nAsSISNode=dynamic_cast<SISNode*>n;
if(nAsSISNode!=0){ nAsSISNode->getStatus(); }

And, assuming that NetworkNode is derived from GenericNetworkNode you
can then use std::list<GenericNetworkNode*> to store them. Though it
might be worth to follow Alf's advice and use some kind of smart pointer
(which would make it std::list<SmartPointerType<GenericNetworkNode> >).
 
J

James Kanze

This is supposed to be Java, I suppose.

The exact equivalent in C++ would be:

SISnode* s ;
SISnode* t ;
NetworkNode* n ;
n = t ;
n->getStatus() ; // won't work

I think his example is supposed to be Java. Otherwise, it won't
compile.

The two important points to remember are that C++ has value
semantics by default, you have to explicitly use pointers or
references to get reference semantics, and that the equivalent
to Java's cast operator in C++ is dynamic_cast.

If they above were C++, he'd not loose any type information.
But he'd get a new object (with a new type), which is probably
not what he wants.

That's valid for entity objects, but not for value objects.
And, assuming that NetworkNode is derived from
GenericNetworkNode you can then use
std::list<GenericNetworkNode*> to store them. Though it might
be worth to follow Alf's advice and use some kind of smart
pointer (which would make it
std::list<SmartPointerType<GenericNetworkNode> >).

If these are entity objects, as it would seem, I don't know of a
smart pointer that would really be appropriate.
 
O

Oliver Graeser

James said:
This is supposed to be Java, I suppose.

Yes, it was supposed to be Java
The exact equivalent in C++ would be:

SISnode* s ;
SISnode* t ;
NetworkNode* n ;
n = t ;
n->getStatus() ; // won't work

Thanks a lot for that! I will consider what Alf Steinbach wrote above on
how to do it with a boost::shared_ptr, but in any case this is exactly
what I was trying to do. The word "dynamic_cast" is actually the key, it
is really hard to google something when you don't know its name ;)
I think his example is supposed to be Java. Otherwise, it won't
compile.


The two important points to remember are that C++ has value
semantics by default, you have to explicitly use pointers or
references to get reference semantics, and that the equivalent
to Java's cast operator in C++ is dynamic_cast.


If they above were C++, he'd not loose any type information.
But he'd get a new object (with a new type), which is probably
not what he wants.
Yup. Actually this was a problem I encountered before when I had an
object a, created a new object b, said b=a, manipulated b and was
stunned that a was still the same.... I'm sorry, I think the question
was probably not really well asked, I tried to keep it concise.
That's valid for entity objects, but not for value objects.



If these are entity objects, as it would seem, I don't know of a
smart pointer that would really be appropriate.

Ayah. Smart pointer, entity objects, value objects - enough for today to
read on. Thanks a lot, everyone!
 
T

Triple-DES

Yes, it was supposed to be Java





Thanks a lot for that! I will consider what Alf Steinbach wrote above on
how to do it with a boost::shared_ptr, but in any case this is exactly
what I was trying to do. The word "dynamic_cast" is actually the key, it
is really hard to google something when you don't know its name ;)

I'd just like to mention that if the dynamic type of *s happened to be
something else (not a SISnode), then this would not work. To ensure
that the cast succeeded, check the pointer against 0.

s = dynamic_cast<SISnode*>(n);
if(!s)
{
// the cast failed, handle error...
}

or:

if( !(s = dynamic_cast<SISnode*>(n)))
{
// handle error...
}

I prefer the former.

DP
 
O

Oliver Graeser

Triple-DES said:
I'd just like to mention that if the dynamic type of *s happened to be
something else (not a SISnode), then this would not work. To ensure
that the cast succeeded, check the pointer against 0.

Thanks for the hint. I'm actually not worried about having nodes with
the wrong type since every network only has one kind of node, I'm going
through this ordeal only because I want to keep all reporting methods in
the same network class.

I used you hint though to work out another problem. I originally stored
all my nodes in an array, such as

GenericNetworkNode * nodeList;//declaration
......

nodeList = new SISNetworkNode[size];//network constructor
......
SISNetworkNode *snn;
snn=dynamic_cast <SISNetworkNode*> (&nodeList);//here comes the disaster

I managed to find out that it is something about the array, i.e. if I
instead use an array of pointers, create a node for each pointer, and
then do the dynamic cast for nodeList instead of &nodeList, it
works. Is there any particular reason why dynamic casts doesn't work
with arrays?
 
T

Triple-DES

I used you hint though to work out another problem. I originally stored
all my nodes in an array, such as

GenericNetworkNode * nodeList;//declaration
.....

nodeList = new SISNetworkNode[size];//network constructor
.....
SISNetworkNode *snn;
snn=dynamic_cast <SISNetworkNode*> (&nodeList);//here comes the disaster

I managed to find out that it is something about the array, i.e. if I
instead use an array of pointers, create a node for each pointer, and
then do the dynamic cast for nodeList instead of &nodeList, it
works. Is there any particular reason why dynamic casts doesn't work
with arrays?


Yes. When you assign an array of SISNetworkNode to a
GenericNetworkNode*, the array decays to a pointer to the first
element. This pointer is then converted to a GenericNetworkNode*. The
fact that this pointer actually points to an array of SISNetworkNode
is "lost" to the compiler.

When you later write nodeList, the compiler will compute the memory
location as if you had an array of GenericNetworkNode, not
SISNetworkNode. Since SISNetworkNode is almost surely larger than
GenericNetworkNode you will end up referencing some random memory
location within the array.

As you have already figured out, arrays of pointers solves the
problem. But it would be even better to use std::vector instead and
avoid arrays.

DP
 
H

HL

I'd just like to mention that if the dynamic type of *s happened to be
something else (not a SISnode), then this would not work. To ensure
that the cast succeeded, check the pointer against 0.

Thanks for the hint. I'm actually not worried about having nodes with
the wrong type since every network only has one kind of node, I'm going
through this ordeal only because I want to keep all reporting methods in
the same network class.

I used you hint though to work out another problem. I originally stored
all my nodes in an array, such as

GenericNetworkNode * nodeList;//declaration
.....

nodeList = new SISNetworkNode[size];//network constructor
.....
SISNetworkNode *snn;
snn=dynamic_cast <SISNetworkNode*> (&nodeList);//here comes the disaster

I managed to find out that it is something about the array, i.e. if I
instead use an array of pointers, create a node for each pointer, and
then do the dynamic cast for nodeList instead of &nodeList, it
works. Is there any particular reason why dynamic casts doesn't work
with arrays?




s = dynamic_cast<SISnode*>(n);
if(!s)
{
  // the cast failed, handle error...
}

if( !(s = dynamic_cast<SISnode*>(n)))
{
  // handle error...
}
I prefer the former.
DP- Hide quoted text -

- Show quoted text -- Hide quoted text -

- Show quoted text -


Oliver, your question is exactly about polymorphism.
polymorphism in C++ is implemented with Pointers and References. You
can't expect polymorphic behavior on objects.
And, since they SisNode and NetworkNode are in the same class
hierarchy, static_cast is supposed to use instead of dynamic_cast. The
latter is usually used in multi-inheritence casting a base to its
sibling. But it's so possible to be fail that Triple-DEC mentioned to
check the result of casting.
 

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,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top