dynamic_cast is ugly!

C

coal

That very much depends on the transport layer, I would think.


And a specialize transport layer only knows a little.  It
doesn't take much specialization to ensure that all of the
objects transported have a common base (i.e.
TransportableObject?), and are polymorphic.

I don't think there is anything gained from doing it that way
and with virtual functions there is probably some perf. hit.
Maybe, maybe not.  There's not necessarily a wire; it's sending
objects between to components.  There are many different types
of transport layer.  (And even if there is a wire, many
protocols will have all transportable objects derive from some
common base type, or a small set of common base types.)


I think you're confusing the transport layer of OSI network
protocols (TCP, etc.) with the transport layer of OO design.


Why?  If you want an object to be transportable, you say so.  It
seems to me to be part of the basic principles of static type
checking.  

The way I do it, you "say so" by adding Send/Receive function
prototypes to a class. The compiler will complain if it finds
implementations for those functions, but the class doesn't have
prototypes for them.
You're transporting message objects between higher
level components which have connected to the transport layer.
You don't (and can't) transport just anything.

There has to be action from the programmer indicating what
types are going to be used in messages, but I don't think that
translates to inheritance being required. Using inheritance
like that seems just to get in the way and makes multiple
inheritance much more likely.

Brian Wood
Ebenezer Enterprises
www.webebenezer.net
 
J

Jeff Schwab

James said:
That very much depends on the transport layer, I would think.


And a specialize transport layer only knows a little. It
doesn't take much specialization to ensure that all of the
objects transported have a common base (i.e.
TransportableObject?), and are polymorphic.


Maybe, maybe not. There's not necessarily a wire; it's sending
objects between to components. There are many different types
of transport layer. (And even if there is a wire, many
protocols will have all transportable objects derive from some
common base type, or a small set of common base types.)


I think you're confusing the transport layer of OSI network
protocols (TCP, etc.) with the transport layer of OO design.

I have been using the terms somewhat interchangeably: not specifically
meaning OSI layer 4, but "the next layer down" from whichever layer is
being discussed. This seemed to be enough for the current discussion,
but by all means let me know if there is a relevant difference that I'm
missing. I'm always (well, usually) happy to learn. :)
Why? If you want an object to be transportable, you say so. It
seems to me to be part of the basic principles of static type
checking. You're transporting message objects between higher
level components which have connected to the transport layer.
You don't (and can't) transport just anything.

The "message" objects you're describing are just utilities. It is not,
IMO, reasonable for a C++ library to dictate what classes must be
derived from by a higher-layer client. The principle applies as
strongly to application-specific libraries as to general-purpose ones.
Once you do this, you are effectively implementing a framework rather
than a library. Especially if multiple such pseudo-libraries are to
co-exist in the same application, there is, I think, a real danger that
the client objects will be forced to implement an unwieldy number of
different, sometimes conflicting, interfaces.

A better solution is for "message" to be a concrete type provided by the
transport layer, such that each message is a configurable object tasked
with carrying an arbitrary bundle of data. It is none of the transport
layer's business what is or is not being sent between two higher-layer
objects. This is one of the few valid uses of void*.

There should also be a way for the message to include meta-data about
its payload, such that type information may be retrieved by the message
recipient. A dynamic_cast may be a convenient way of achieving some of
this functionality (if void* is replaced with e.g. transportable*), but
it is not a general solution; seeing dynamic_cast used to retrieve
run-time type information after transport would set off alarms in my
mind, and would seem a good reason to scrutinize both the transport and
application layers' design.

There is, of course, the Java-style design option to have run-time
tagging interfaces of the java.io.Serializable variety. I do not care
for such interfaces even in Java. I note that they are a frequent
source of confusion to new Java programmers, and I would not be happy to
see them rearing their wart-covered heads in C++.
 
J

Jeff Schwab

Andy said:
Well for one thing I was working in Assembler, not C++. No high level
language that I'm aware of will give you that much control.

You can write (or use a library of) platform-specific macros and
functions. A couple of years ago, I used the ones that came with an
ancient copy of Borland 5 for PC-DOS on 32-bit x86. It's still useful,
but I would like to find a modernized version, since the C++ compiler is
pre-standard. (While we're on the topic, I'd also like a C++ compiler
that supported big real/unreal/32-bit real mode. 64-bit addressing in
real mode would also be nice, but I don't even know how to get that in
assembly.)
The return?

mov ax,esp ; or bp
jmp [ax]

I think it goes. But my assembler books are at work, and I haven't
written much lately.

I'm with you on both counts! That looks correct-ish, though, for a near
jump.
And no, you can't write assembler without some sort of goto, even when
you are using it to emulate a cleaner structure.

You can once you get stack. Modern x86 chips have not only explicit
instructions for procedure call and return, but enter and exit
instruction that automatically prepare and clean up each stack frame.
OTOH, you don't have a
dynamic_cast either, which is where I started this thread!

True. :) You do have its moral equivalent, which is the comparison of
one or more function-table addresses.
 
J

Jerry Coffin

[ ... ]
this seems to be specifically waht Juha was objecting to!!
The Shape class ends up with a very application specific

turn_square_yellow()

method. I can understand his horror! Even if we try a more
real world UML editor.

hilight_interface()

that still clutters the base class with UI issues. And sub-class
specific calls. I'm a beginner I looking for design advice!

As I noted elsethread, I'd start from the fact that as we're postulating
the design, we're using a shared action among all objects of a given
type to simulate sharing state among all objects of a given type.

If we want objects to act like they're sharing state, I think we should
express that directly rather than simulating it by sharing an action
across those objects. My earlier post contained an implementation that
answered the question that was asked, but wasn't (IMO) particularly
extensible.

Realistically, we know that if you want to be able to highlight objects
of one type, you probably also want to be able to highlight objects of
other types as well -- in fact, you _probably_ want to be able to
highlight objects of _any_ type.

The approach I used elsethread _could_ do that, but would require code
duplicated across all the descendants of 'shape' to do so (to create and
manipulate a static variable in each). Realistically, we also know that
there's more to the UI of an object than just its current color. That
implies that there would be quite a lot of code duplicated across the
hierarchy, something we'd generally much rather avoid.

To accomplish this, we probably want to start by creating a UI class
that describes the current UI state of an object. We create an instance
of this UI class for each class of object in our hierarchy. When we
create each object in the hierarchy, we include a pointer (or reference)
to the appropriate UI state object that's shared among all objects of
that type. When we want to (for example) highlight objects of a
specified type, we do NOT manipulate the individual objects of that type
-- rather, we manipulate the UI state object shared by all objects of
that type:

class UI_state {
enum { NORMAL, HIGHLIGHTED } highlight_state;
color colors[2];
int x_, y_;
/* other UI state here */

public:
highlight() { highlight_state = HIGHLIGHTED; }
normal() { highlight_state = NORMAL; }

color current_color() const { return colors[highlight_state]; }
};

enum UML_objects {
UML_CLASS,
UML_INTERFACE,
UML_COMPONENT,
/* ... */
UML_OBJECT_LAST
};

std::vector<UI_state> UI_states(UML_object_last);

class UML_object {
UI_state &ui_;
public:
virtual void draw(surface_t &surface) const = 0;
}

class UML_class : public UML_object {
public:
UML_class(int x, int y)
: x_(x), y_(y), ui_(UI_states[UML_CLASS])
{}
virtual void draw(surface_t &surface) const {
// draw self onto surface using ui
}
};

class UML_interface : public UML_object {
public:
UML_interface(int x, int y)
: x_(x), y_(y), ui(UI_states[UML_INTERFACE])
{}

virtual void draw(surface_t &surface) const {
// draw self onto surface using ui
}
};

Now, to highlight all interfaces, we do NOT look through our collection
of objects looking for interface objects, and then manipulate the color
of each. Instead, 'UI_states[UML_INTERFACE].highlight()' does the whole
job at once. Highlighting objects of a different type obviously requires
nothing more than choosing the appropriate constant. In any case, we're
manipulating a single shared state instead of searching for a set of
objects with duplicated state.

I should add that I doubt this design would really apply to a UML
editor. I can't remember ever having wanted to highlight all objects of
a given type. Rather, highlighting would typically be based on a
relationship, such as "everything that inherits from X", or "everything
that implements Y", or "everything that depends on Z". As such, you're
probably going to determine the highlight state based on the
relationships defined in the model, not simply the type of an object.

As such, I think the design is probably for something nobody has ever
(and probably will ever) want, at least as presented. OTOH, I can
imagine situations where such a thing really would be useful, and for
such a situation, I think this is a much cleaner implementation than
anything using dynamic_cast.

IMO, this design is much more adaptable to a real-world scenario. If I
was designing a UML editor, I don't think I'd use a static class
hierarchy to represent the UML elements. Rather, I'd put almost
everything related to each UML element into initalization files of some
sort, and simply load those up during program startup. For example, an
implementation on Window would load a series of metafiles, and when it
needed to draw an object, it would use PlayMetafile or PlayEnhMetafile
to do the job.

Using this design, all the UML objects might easily be of precisely the
same type. Sharing the UI state would remain easy, because when we
create an object we know what kind of object we're creating, and we only
need to use a suitable value to relate that object to the appropriate UI
state. In this situation, dynamic_cast wouldn't work at all -- it would
do nothing to distinguish between one UML element and another, since
from the viewpoint of C++ they would all just be objects of a single
type.
 
D

Daniel T.

Andy Champ said:
Ah well it was an idea.  It appears our experience is in different
domains.  I've never used Python or Smalltalk, and hardly touched Java!

I think this is part of the disconnect in this thread, and others like
it, for me. My domain is not business/database apps like many of the
others in this discussion. I am a video game programmer, so most of my
work is in the simulation domain... often on systems that are much
like what embedded programmers have to deal with.

OO programming's roots are in simulation work so I sometimes think
that I'm more steeped in the paradigm than many others.
 
D

Daniel T.

(e-mail address removed) says...


I would posit that (at least as stated) this would constitute quite a
poor design.

In many cases, yes it would. There are almost as many ways to design
the system as there are systems. :)
Instead, if we want to be able to highlight all the interfaces (or
whatever) we'd share the "highlighted" vs. "normal" state (or perhaps
the current color) among all objects of that type:

struct UML_object {
        int x_, y_;
        virtual void draw(surface_t &) = 0;

};

class UML_interface : public UML_object {
        static color_t color;
public:
        static void highlight(bool state=true) {
                static color_t colors[] = {
                        RGB(0,0,0),
                        RGB(255, 0,0)
                };

                color = colors[state];
                // code to force re-draw goes here.
        }

        square(int x, int y) :  x_(x), y_(y) {}

        virtual void draw(surface_t &s) {
                // draw "interface" on specified surface
        }

};

class UML_class : public UML_object {
public:
        UML_class(int x, int y) : x_(x), y_(y) {}

        virtual void draw(surface_t &s) {
                // draw "class" on specified surface
        }

};

color_t UML_interface::color;

This way we don't need separate containers OR a dynamic_cast to
highlight all your UML_interface objects -- instead, you call:
UML_interface::highlight();
and they all highlight. To change them all back to normal, you call:
UML_interface::highlight(false);

IMO, if you want shared state, it's better to create real shared state
than to force all objects of that type to the same state, independently
of each other.

Excellent post! Another way would be to have some sort of state object
that each of the elements literally share, kind of like a style sheet.
That way it would be easy to support exceptions to the rule, simply
give them a different/non-standard style sheet.
 
D

Daniel T.

  You are completely missing the point. I am not defending dynamic
casting. I am *not* saying that dynamic casting is good or the correct
way to do this. I'm asking for a better alternative, in C++. I'm getting
none.

  The closest thing I have got so far is a suggestion which sounds like
implementing the delegation paradigm (which some other OO languages
support). How to implement this without dynamic cast has not been
specified in any way.

Jerry Coffin recently submitted two posts that provide concrete code
that may help you.
 
J

James Kanze

I don't think there is anything gained from doing it that way
and with virtual functions there is probably some perf. hit.

There's a definite advantage in that you state up front which
objects are transportable. In many cases, such objects may have
to fulfill additional requirements as well. Deriving from a
common base allows some additional compile time checking.

As for the performance hit, be serious. You're going through an
extra layer; that will cost something in terms of runtime. More
than any virtual function call, anyway.
The way I do it, you "say so" by adding Send/Receive function
prototypes to a class. The compiler will complain if it finds
implementations for those functions, but the class doesn't have
prototypes for them.

Which can work too, but is less sure than if the programmer
explicitly states that he implements the TransportableObject
contract. And at least in C++, doing this way means using
templates, with the resulting increase in coupling, code bloat
and explosion of compile times. (This would be partially
mitigated if your compiler supports export, but the code bloat
and some additional coupling is inevitable.)
There has to be action from the programmer indicating what
types are going to be used in messages, but I don't think that
translates to inheritance being required. Using inheritance
like that seems just to get in the way and makes multiple
inheritance much more likely.

Inhertance remains the simplest and most efficient means of
specifying conformance to a contract. It does get in the way of
the programmer accidentally creating a class which conforms to
the superficial aspects (presence of such and such a function),
but being unaware of the actual contract, and not implementing
the desired semantics, yes.

And of course, multiple inheritance is a fact of life as soon as
you have static type checking. Big deal.
 
J

James Kanze

I have been using the terms somewhat interchangeably: not specifically
meaning OSI layer 4, but "the next layer down" from whichever layer is
being discussed.

OK. That's the sense I'm using as well. (I probably should
have chosen a different term, since transport layer does suggest
the OSI layer 4, and the lower layer isn't always involved with
"transport", in the strictest sense---just hooking components
together somehow.)
This seemed to be enough for the current discussion, but by
all means let me know if there is a relevant difference that
I'm missing. I'm always (well, usually) happy to learn. :)

The OSI layer 4 is a special case of the general case.
The "message" objects you're describing are just utilities.
It is not, IMO, reasonable for a C++ library to dictate what
classes must be derived from by a higher-layer client.

It's not reasonable for it not to. A C++ library must specify
very clearly which classes are designed to be used as base
classes, and in what ways. By default, the assumption is (or
should be) that you cannot reasonable derive from a library
class. The exceptions should be clearly documented.
The principle applies as strongly to application-specific
libraries as to general-purpose ones. Once you do this, you
are effectively implementing a framework rather than a
library.

In an application, the lower levels are a framework. Or part of
one.
Especially if multiple such pseudo-libraries are to co-exist
in the same application, there is, I think, a real danger that
the client objects will be forced to implement an unwieldy
number of different, sometimes conflicting, interfaces.

If the interfaces conflict, then you do have a problem. I've
not found this to be a problem in practice, however.
A better solution is for "message" to be a concrete type
provided by the transport layer, such that each message is a
configurable object tasked with carrying an arbitrary bundle
of data. It is none of the transport layer's business what is
or is not being sent between two higher-layer objects. This
is one of the few valid uses of void*.

Which is, IMHO, even less type-safe than using a common base
class. And more or less requires the client code to implement
the equivalent of dynamic_cast itself: a lot of extra work, and
additional potential for errors.
There should also be a way for the message to include meta-data about
its payload, such that type information may be retrieved by the message
recipient. A dynamic_cast may be a convenient way of achieving some of
this functionality (if void* is replaced with e.g. transportable*), but
it is not a general solution; seeing dynamic_cast used to retrieve
run-time type information after transport would set off alarms in my
mind, and would seem a good reason to scrutinize both the transport and
application layers' design.

dynamic_cast is never a general solution. I don't think a
"general solution" exists for this problem. dynamic_cast is
part of an appropriate solution in some specific cases. In
others, of course, you'll use other tools.
There is, of course, the Java-style design option to have run-time
tagging interfaces of the java.io.Serializable variety.

Java's serialization is broken. I've never found a case where
it was an appropriate solution. Probably because it attempts to
do too much.

But I'm not sure it's relevant here. The "framework" or the
"transport layer" we're talking about doesn't necessarily
involve serialization.
 
J

James Kanze

Post the problem statement that requires the above, post some
code doing it and maybe we can provide a better solution.

Two concrete cases where using dynamic_cast seems to be
preferable to the alternatives:

-- A network element implementing connections, which is part of
a distributed management information base (MIB). A
connection is specified by means of an external create
request for a cross connection object (CC---in the
application domain, such abbreviations are widely used and
understood). A connection is established between to
termination points (TP), which must have been created
previously, and are specified by means of distinguished
names (DN). In the application framework, of course, you
have a data dictionary mapping DNs to managed objects (MO);
the constructor of CC (which derives from MO itself, of
course, as does TP and anything else which can be accesses
through the MIB) requests pointers to the corresponding TPs,
using the DNs: the data dictionary returns an MO*, which it
dynamically casts to a TP*---if the dynamic_cast fails, it
throws an exception, generating an error response to the
creation request.

In practice, the code would probably want to distinguish
between a lookup failure (the data dictionary returned a
null pointer) and a type failure (the dynamic_cast failed).
In addition, there are different types of TP's, the code
would go on to verify that the source and the sink were
compatible with one another. This probably shouldn't
involve dynamic_cast---the role of a TP is to be a source or
a sink, and the type and direction of the date stream is a
characteristic of all TPs, so having a virtual member
function which returns this information in the TP class
seems a more reasonable solution.

-- A GUI framework allows you to connect listeners to handle
specific events. In the case of some events, you want to do
something common to all of the buttons in a toolbar (maybe
disenable them, say until some exteral operation has
finished). So you get the toolbar, and iterate over its
components. You've constructed the toolbar, so you know
that all of its components are in fact MyButton (although of
course, for the generic toolbar code and the GUI framework
in general, they're just GUIComponent). In this case, you
use dynamic_cast to convert the GUIComponent* you get back
from the toolbar to a MyButton*, in order to call
MyButton::disable() on it.

In this case, you could argue that you should also derive
from GUIToolBar, and maintain a container of MyButton* in
parallel with it, in order not to loose the type when you
inserted the MyButton into the toolbar. This, of course,
avoids the dynamic_cast, But this means a lot of extra code,
and a significantly increased possibility of errors (the
container in the derived toolbar class getting out of sync
with the one in the base GUIToolBar class). As designs go,
it's not as good as the one using the dynamic_cast.
 
A

Andy Champ

Jeff said:
You can write (or use a library of) platform-specific macros and
functions. A couple of years ago, I used the ones that came with an
ancient copy of Borland 5 for PC-DOS on 32-bit x86. It's still useful,
but I would like to find a modernized version, since the C++ compiler is
pre-standard. (While we're on the topic, I'd also like a C++ compiler
that supported big real/unreal/32-bit real mode. 64-bit addressing in
real mode would also be nice, but I don't even know how to get that in
assembly.)
The return?

mov ax,esp ; or bp
jmp [ax]

I think it goes. But my assembler books are at work, and I haven't
written much lately.

I'm with you on both counts! That looks correct-ish, though, for a near
jump.
And no, you can't write assembler without some sort of goto, even when
you are using it to emulate a cleaner structure.

You can once you get stack. Modern x86 chips have not only explicit
instructions for procedure call and return, but enter and exit
instruction that automatically prepare and clean up each stack frame.

Well IIRC the order of operations was:

- walk ones and zeroes through all registers
- Test base 64Kb of RAM

If either failed, put out a message through the might-be-operating
serial port, then loop repeating the test so an oscilloscope can be used
for diagnosis

- Initialise stack into base 64Kb

then you can do more normal stuff,like local variable and calls.

You are of course running in 8086 emulation mode, this is straight after
power-on reset.

Andy
 
R

Robert Martin

In my case a lot of the operations can be done without dynamic
casting, using virtual functions. There are some operations, however,
which just can't.

Remember that since dynamic_cast can be implemented with nothing but
virtual functions, anything that can be solved with dynamic_cast can
also be solved with virtual functions. It's just that sometimes
dynamic_cast is the more convenient and more elegant choice.
 
R

Robert Martin

I don't suggest that the above is bad design. In fact I mentioned it as
a valid use of dynamic_cast.

However, that doesn't speak to the point of the issue. In the example
given you have a container of Shapes and you *don't* know which of them,
if any, are Squares.

To do a dynamic_cast in that case is fishing.

void zapAllSquares(listOfShapes) {
vector<Shape*>::iterator i;
for (i=listOfShapes.begin(); i != listOfShapes.end(); i++) {
Shape* s = *i;
Square* sq = dynamic_cast<Square*>(s);
if (sq)
sq->zap();
}
}

I have no objection to the above code. The function is all about
squares. It calls 'zap' which is a function defined upon Square, and
only on Square. The function depends on the transitive closure of
classes that Square depends on, and therefore no dependencies can be
removed by any other design.
 
R

Robert Martin

2) In some cases I have to traverse *all* the objects in the order in
which they are in the main container and perform *type-specific*
operations to them (iow. operations which cannot be specified as virtual
functions in the base class). It would not be enough to traverse the
type-specific containers one after another (because it would mean that
the objects are traversed out-of-order with respect to the main container).

That sounds like a job for Acyclic Visitor. Which is just the Visitor
pattern implemented with dynamic_cast. The benefit is that rather than
do a nasty if/else, you only do *one* dyamic_cast, followed by a
virtual function call. So you minimize the time uncertainty.
 
J

Juha Nieminen

Daniel said:
You don't know how to make a virtual function in a base class?

You want me to add a virtual function named "changeSquareColor" to the
Shape base class if I want to be able to change the color of squares?

That breaks object-oriented design quite horribly. For one, it breaks
the "is-a" relationship. More precisely, it breaks the "behaves like"
property of an "is-a" relationship: If you specify that "Shape" supports
the functionality "changeSquareColor", you are effectively saying that
*all* classes derived from Shape support that functionality. However,
only one does, the others don't.

The base class would be cluttered with functions specific to some
derived classes. And besides, in some cases you *can't* modify the base
class (eg. because it's in a library).
 
J

Juha Nieminen

Jerry said:
struct UML_object {
int x_, y_;
virtual void draw(surface_t &) = 0;
};

class UML_interface : public UML_object {
static color_t color;
public:
static void highlight(bool state=true) {
static color_t colors[] = {
RGB(0,0,0),
RGB(255, 0,0)
};

color = colors[state];
// code to force re-draw goes here.
}

square(int x, int y) : x_(x), y_(y) {}

virtual void draw(surface_t &s) {
// draw "interface" on specified surface
}
};

class UML_class : public UML_object {
public:
UML_class(int x, int y) : x_(x), y_(y) {}

virtual void draw(surface_t &s) {
// draw "class" on specified surface
}
};

I don't really understand what you are doing there. It's not even
valid C++. (And public member variables?)
This way we don't need separate containers OR a dynamic_cast to
highlight all your UML_interface objects

Maybe one of us is missing the point here?

The problem is not how to implement a feature which is common to all
the objects. The problem is how to implement a feature which is not.

Of course if a feature is common to all the objects (such as your
highlighting above), you simply make that feature part of the base
class. The problem is that not all features are common to all objects
and cannot be logically placed in the base class.
 
J

Juha Nieminen

Daniel said:
Excellent post!

All I understood from his post was: If you have a feature which is
common to *all* the objects, put that feature in the base class.

Well, duh. Hello? That isn't the issue here at all. (*Of course* I do
that every time I can. You don't have to tell me that.)

The issue is that some derived classes might have features which do
not belong to the base class at all. (And, in some cases, couldn't even
be added there.)
 
P

Phlip

Juha said:
All I understood from his post was: If you have a feature which is
common to *all* the objects, put that feature in the base class.

Well, duh. Hello? That isn't the issue here at all. (*Of course* I do
that every time I can. You don't have to tell me that.)

Uh, actually you shouldn't do that. Most re-use should move out to
delegates, not up to base classes.
 
K

kwikius

  All I understood from his post was: If you have a feature which is
common to *all* the objects, put that feature in the base class.

  Well, duh. Hello? That isn't the issue here at all. (*Of course* I do
that every time I can. You don't have to tell me that.)

  The issue is that some derived classes might have features which do
not belong to the base class at all. (And, in some cases, couldn't even
be added there.)


FWIW Using dynamic_cast is fine in my book. It does affect
performance for complicated hierarchies, and if that is important I
would add a virtual getID() function in base class. Use the getID()
function to ascertain the class is a form of square or whatever,and
then use dynamic cast, if necessary. Often though dynamic_cast works
well enough without adding an ID.

Another approach is a union of pointers with an ID. This can reduce
the size of objects over trying to mung them into some common base
class when they are only loosely related, but need to be grouped
together in a container.

Fact is that how well objects are related is actually highly variable,
contrary to what OOP afficionados would have us belive. dynamic
polymorphism is a theory beloved of academics.

Being cynical OOP is a useful kludge to make different entities look
the same to satisfy type checking, generally when you have to put them
in a collection. It has a big effect because you have to use pointers
and mostly that implies allocating on the heap, and a big loss in
preformance over using value_types.

It works because its a simple "elegant" solution to a complicated
problem, but it shouldnt be seen as perfect. The types "ARE"
different, so outside the ideal world of academia and textbooks,
dynamic_cast is required to discriminate. If dynamic_cast was
unneccessary, it wouldnt exist.

regards
Andy Little
 
L

lbonafide

The issue is that some derived classes might have features which do
not belong to the base class at all. (And, in some cases, couldn't even
be added there.)

Which is why I wouldn't generally put those Derived in a collection of
Base *, but in a collection of Derived *.
 

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,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top