PHWinfo banniere

Titres
PORTAIL ANNUAIRE ARTICLES COMPARATEUR HÉBERGEURS DEVIS FORUMS RÉDUCTEUR D'URL
Précédent   PHWinfo > Autres forums > Forum Programmation & Conception > comp.lang.cplus > OOP question: Is upcasting ok in this situation?
S'inscrire FAQ Membres Recherche Messages du jour Marquer les forums comme lus
OOP question: Is upcasting ok in this situation?

Réponse
 
LinkBack Outils de la discussion
Vieux 16/01/2008, 19h46   #1
Juha Nieminen
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut OOP question: Is upcasting ok in this situation?

If I'm not mistaken, general OOP wisdom says that upcasting should
usually be avoided if possible. I have a situation, however, where I
can't think of a better way than upcasting for this specific solution.
I was wondering if there exists a "standard" solution to this which
avoids the upcasting and does it more cleanly.

Assume that we have, for example, some kind of container/editing class
named PrimitiveEditor which handles objects of (the abstract) type
Primitive. So you can have, for example, classes like:

class Circle: public Primitive { ... };
class Square: public Primitive { ... };
etc.

The PrimitiveEditor defines a callback interface, let's call it
PrimitiveHandlingCallback, and it's used, for example, like this:

class MyClass: public PrimitiveHandlingCallback
{
PrimitiveEditor editor;
std::list<Circle> circles;
std::list<Square> squares;

public:
MyClass(): editor(this) {}

// Derived from PrimitiveHandlingCallback:
virtual void doSomethingToPrimitive(Primitive*);
};

Now, each time MyClass creates an instance of Circle or Square (into
the correspondent list) it gives a pointer to that instance to the
editor. The editor is called to handle those objects. The editor may
call the doSomethingToPrimitive() function so that MyClass can handle
the primitives in whatever special way it needs to.

And this is where my question raises. Is it ok to do it like this:

void MyClass::doSomethingToPrimitive(Primitive* p)
{
Circle* circle = dynamic_cast<Circle*>(p);
if(circle)
{
// Do something with circle, specific to the type Circle
return;
}

Square* square = dynamic_cast<Square*>(p);
if(square)
{
// Do something with square, specific to the type Square
return;
}
}

Is there any better/cleaner way of doing this same thing without
having to upcast?
  Réponse avec citation
Vieux 16/01/2008, 20h16   #2
Alf P. Steinbach
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: OOP question: Is upcasting ok in this situation?

* Juha Nieminen:
> If I'm not mistaken, general OOP wisdom says that upcasting should
> usually be avoided if possible.


I think you mean downcasting, not upcasting.


> I have a situation, however, where I
> can't think of a better way than upcasting for this specific solution.
> I was wondering if there exists a "standard" solution to this which
> avoids the upcasting and does it more cleanly.
>
> Assume that we have, for example, some kind of container/editing class
> named PrimitiveEditor which handles objects of (the abstract) type
> Primitive. So you can have, for example, classes like:
>
> class Circle: public Primitive { ... };
> class Square: public Primitive { ... };
> etc.
>
> The PrimitiveEditor defines a callback interface, let's call it
> PrimitiveHandlingCallback, and it's used, for example, like this:
>
> class MyClass: public PrimitiveHandlingCallback
> {
> PrimitiveEditor editor;
> std::list<Circle> circles;
> std::list<Square> squares;
>
> public:
> MyClass(): editor(this) {}
>
> // Derived from PrimitiveHandlingCallback:
> virtual void doSomethingToPrimitive(Primitive*);


Prefer passing argument by reference, unless you specifically want to
support nullpointers.

> };



>
> Now, each time MyClass creates an instance of Circle or Square (into
> the correspondent list) it gives a pointer to that instance to the
> editor.


Why not a reference?


> The editor is called to handle those objects. The editor may
> call the doSomethingToPrimitive() function so that MyClass can handle
> the primitives in whatever special way it needs to.
>
> And this is where my question raises. Is it ok to do it like this:
>
> void MyClass::doSomethingToPrimitive(Primitive* p)
> {
> Circle* circle = dynamic_cast<Circle*>(p);
> if(circle)
> {
> // Do something with circle, specific to the type Circle
> return;
> }
>
> Square* square = dynamic_cast<Square*>(p);
> if(square)
> {
> // Do something with square, specific to the type Square
> return;
> }
> }
>
> Is there any better/cleaner way of doing this same thing without
> having to upcast?


Your example of PrimitiveHandlingCallback and PrimitiveEditor is quite
confusing.

But essentially it seems your question reduces to how to do
type-specific things to objects in an heterogenous container.

In the simplest case, some common type for those objects will simply
provide a virtual function:

struct ContainedObject
{
virtual ~ContainedObject() {}
virtual void someOperation() {}
};

One level up in complexity, it may be that someOperation() will only be
meaningful for a subset of the object types. And then it's an
engineering decision whether to provide this operation in
ContainedObject, with failure for objects that don't support it, or
whether to e.g. signal presence of that functionality via an interface.
The interface way might look like

struct ISomeOperation
{
virtual ~ISomeOperation() {}
virtual void someOperation() = 0;
};

struct ContainedObject
{
virtual ~ContainedObject() {}
};

struct Square: ContainedObject, virtual ISomeOperation
{
virtual void someOperation() {}
};

void foo( ContainedObject& o )
{
ISomeOperation* pInterface = dynamic_cast<ISomeOperation*>( &o );
if( pInterface != 0 ) { pInterface->someOperation(); }
}

Then further up in complexity it might be that the type-specific
operation to be performed uses knowledge that only resides in the
"calling" object, e.g. the container object.

In that case the downcasting can be centralized in someOperation, which
handed a general Container references calls back on type-specific member
functions:


struct Square;
struct Circle;
struct Container;

struct ContainedObject
{
virtual ~ContainedObject() {}
virtual void someOperation( Container& ) {}
};

struct Container
{
virtual void handle( Square& );
virtual void handle( Circle& );

void foo( ContainedObject& o )
{
o.someOperation( *this );
}
};

struct Square: ContainedObject
{
void someOperation( Container& c ) { c.handle( *this ); }
}

This "ask for callback" pattern is generally known as the visitor
pattern. Some people then prefer to use the name "visit" instead of
"someOperation", i.e., dear object, please visit me, passing your
esteemed self as argument.

Cheers, & hth.,

- Alf


--
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?
  Réponse avec citation
Réponse


Outils de la discussion

Règles de messages
Vous ne pouvez pas créer de nouvelles discussions
Vous ne pouvez pas envoyer des réponses
Vous ne pouvez pas envoyer des pièces jointes
Vous ne pouvez pas modifier vos messages

Les balises BB sont activées : oui
Les smileys sont activés : oui
La balise [IMG] est activée : oui
Le code HTML peut être employé : non
Trackbacks are oui
Pingbacks are oui
Refbacks are oui


Fuseau horaire GMT +1. Il est actuellement 22h51.


Édité par : vBulletin® version 3.7.3
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.2.0 RC5 Tous droits réservés.
Version française #16 par l'association vBulletin francophone
PHWinfo est un site Éducation Sans Frontières ©2000-2008
Ad Management by RedTyger
©Tous droits réservés par les parties respectives
Page generated in 0,12386 seconds with 10 queries