49 votes

Utilisation pratique de dynamic_cast?

J'ai une question assez simple à propos de l'opérateur dynamic_cast . Je sais que cela est utilisé pour l'identification du type d'exécution, c'est-à-dire pour connaître le type d'objet au moment de l'exécution. Mais à partir de votre expérience en programmation, pouvez-vous donner un scénario réel dans lequel vous deviez utiliser cet opérateur? Quelles étaient les difficultés sans l'utiliser?

50voto

BjoernD Points 2712

Jouet exemple

L'arche de noé doit fonctionner comme un conteneur pour les différents types d'animaux. Comme l'arche elle-même n'est pas concerné par la différence entre les singes, les pingouins, et les moustiques, vous définissez une classe Animal, et d'en tirer les classes Monkey, Penguin, et Mosquito de, et de la stocker chacun d'eux Animal dans l'arche.

Une fois le déluge est plus, Noé veut distribuer des animaux à travers la terre et les lieux où ils appartiennent, et par conséquent, les besoins de connaissances supplémentaires sur le générique des animaux stockés dans son arche. À titre d'exemple, il peut maintenant essayer d' dynamic_cast<> chaque animal à un Penguin afin de déterminer lequel des animaux sont des pingouins à être libéré dans l'Antarctique et qui ne le sont pas.

Exemple réel

Nous avons mis en place une surveillance des événements cadre, lorsqu'une demande de stocker à l'exécution, les événements générés dans une liste. Des moniteurs d'événements pourrait passer par cette liste et examiner les événements qu'ils étaient intéressés. Les types d'événements ont été au niveau OS des choses telles que SYSCALL, FUNCTIONCALL, et INTERRUPT.

Ici, nous stockons tous nos événements spécifiques dans une liste générique d' Event des cas. Des moniteurs permettrait de parcourir cette liste et dynamic_cast<> les événements qu'ils ont vu ces types ils ont été intéressés. Tous les autres (ceux qui soulèvent une exception) sont ignorés.

Question: Pourquoi ne pouvez-vous pas avoir une liste séparée pour chaque type d'événement?

Réponse: Vous pouvez faire cela, mais il permet d'étendre le système avec de nouveaux événements ainsi que des nouveaux moniteurs (en agrégeant plusieurs types d'événements) plus difficile, parce que tout le monde doit être au courant des listes respectives à vérifier.

11voto

Kerrek SB Points 194696

Un exemple typique d'utilisation est le modèle de visiteur :

 struct Element
{
    virtual ~Element() { }

    void accept(Visitor & v)
    {
        v.visit(this);
    }
};

struct Visitor
{
    virtual void visit(Element * e) = 0;
    virtual ~Visitor() { }
};


struct RedElement : Element { };
struct BlueElement : Element { };
struct FifthElement : Element { };


struct MyVisitor : Visitor
{
    virtual void visit(Element * e)
    {
        if (RedElement * p = dynamic_cast<RedElement*>(e))
        {
             // do things specific to Red
        }
        else if (BlueElement * p = dynamic_cast<BlueElement*>(e))
        {
             // do things specific to Blue
        }
        else
        {
             // error: visitor doesn't know what to do with this element
        }
    }
};
 

Maintenant, si vous avez des Element & e; , vous pouvez faire MyVisitor v; et dire e.accept(v) .

La principale caractéristique de la conception est que si vous modifiez votre hiérarchie Element , il vous suffit de modifier vos visiteurs. Le modèle est encore assez complexe et recommandé uniquement si vous avez une hiérarchie de classes très stable de Element s.

3voto

Linuxios Points 16966

Imaginez cette situation: Vous avez un programme C++ qui lit et affiche le code HTML. Vous avez une classe de base HTMLElement qui a une méthode virtuelle pure displayOnScreen. Vous avez également une fonction appelée renderHTMLToBitmap, ce qui attire le code HTML d'une image bitmap. Si chaque HTMLElement a vector<HTMLElement*> children;, vous pouvez simplement transmettre l' HTMLElement représentant l'élément <html>. Mais si quelques-uns des sous-classes ont besoin d'un traitement spécial, comme <link> pour l'ajout de CSS. Vous avez besoin d'un moyen de savoir si un élément est un LinkElement de sorte que vous pouvez les donner à l'CSS fonctions. Pour le savoir, il vous suffit d'utiliser dynamic_cast.

Le problème avec dynamic_cast et le polymorphisme en général, c'est qu'il n'est pas très efficace. Lorsque vous ajoutez vtables dans le mélange, il n'est de pire en pire.

Lorsque vous ajoutez des fonctions virtuelles à une classe de base, quand ils sont appelés, en fin de compte vous fait passer par tout un quelques couches de pointeurs de fonction et les zones de mémoire. Qui ne sera jamais plus efficace que quelque chose comme l'ASM call enseignement.

Edit: En réponse à André commentaire ci-dessous, voici une nouvelle approche: au Lieu de dynamique de moulage de l'élément spécifique de type (LinkElement), au lieu de cela, vous avez une autre sous-classe abstraite de l' HTMLElement appelés ActionElement qui remplace displayOnScreen avec une fonction qui n'affiche rien, et crée une nouvelle fonction virtuelle pure: virtual void doAction() const = 0. L' dynamic_cast est changé à l'essai pour l' ActionElement et simplement les appels téléphoniques doAction(). Vous auriez le même type de sous-classe pour GraphicalElement avec une méthode virtuelle displayOnScreen().

Edit 2: Voici ce qu'est un "rendu" de la méthode pourrait ressembler à:

void render(HTMLElement root) {
  for(vector<HTLMElement*>::iterator i = root.children.begin(); i != root.children.end(); i++) {
    if(dynamic_cast<ActionElement*>(*i) != NULL) //Is an ActionElement
    {
      ActionElement* ae = dynamic_cast<ActionElement*>(*i);
      ae->doAction();
      render(ae);
    }
    else if(dynamic_cast<GraphicalElement*>(*i) != NULL) //Is a GraphicalElement
    {
       GraphicalElement* ge = dynamic_cast<GraphicalElement*>(*i);
       ge->displayToScreen();
       render(ge);
    }
    else
    {
      //Error
    }
  }
}

2voto

Alexandre C. Points 31758

Opérateur de dynamic_cast résout le même problème que la distribution dynamique (fonctions virtuelles, des habitudes des visiteurs, etc): elle vous permet d'effectuer des actions différentes en fonction du runtime type d'un objet.

Cependant, vous devez toujours préférer la distribution dynamique, sauf peut-être quand le nombre d' dynamic_cast vous auriez besoin de ne grandira jamais.

Par exemple. vous ne devriez jamais faire:

if (auto v = dynamic_cast<Dog*>(animal)) { ... }
else if (auto v = dynamic_cast<Cat*>(animal)) { ... }
...

pour la maintenabilité et les raisons de performances, mais vous pouvez le faire par exemple.

for (MenuItem* item: items)
{
    if (auto submenu = dynamic_cast<Submenu*>(item))
    {
        auto items = submenu->items();
        draw(context, items, position); // Recursion
        ...
    }

    else
    {
        item->draw_icon();
        item->setup_accelerator();
        ...
    }
}

que j'ai trouvé très utile dans cette situation exacte: vous avez un très particulier subhierarchy qui doivent être traités séparément, c'est là que dynamic_cast rayonne. Mais des exemples du monde réel sont assez rares (exemple de menu est quelque chose que j'avais à traiter).

1voto

Dimitris Staikos Points 116

dynamic_cast est pas conçu comme une alternative à des fonctions virtuelles.
dynamic_cast a un non-trivial de performance, les frais généraux (ou du moins je le pense) depuis l'ensemble de la hiérarchie de classe doit être parcouru à travers.
dynamic_cast est similaire à l 'est d' " opérateur de C# et de la QueryInterface de la bonne vieille COM.

Jusqu'à présent, j'ai trouvé un réel usage de dynamic_cast:
(*) Vous avez de l'héritage multiple et de localiser la cible de la distribution, le compilateur doit marcher de la hiérarchie de classe en haut et en bas pour trouver la cible (ou le bas et le haut, si vous préférez). Cela signifie que la cible de la distribution est à une branche parallèle par rapport à l'endroit où la source de la distribution est à la hiérarchie. Je pense qu'il n'y a PAS d'autre moyen de faire un tel casting.

Dans tous les autres cas, il vous suffit d'utiliser certains de la classe de base virtuelle à vous dire quel type d'objet que vous avez, et ALORS SEULEMENT, vous dynamic_cast à la classe cible de sorte que vous pouvez utiliser certains des non-virtuel fonctionnalité. Idéalement, il devrait y avoir aucune non-virtuel de fonctionnalités, mais ce que le diable, nous vivons dans le monde réel.

En faisant des choses comme:

    if (v = dynamic_cast(...)){} else if (v = dynamic_cast(...)){} else if ...

les performances des déchets.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X