Question

Supposons que j'ai 3 classes comme suit (comme ceci est un exemple, il ne compile pas!):

class Base
{
public:
   Base(){}
   virtual ~Base(){}
   virtual void DoSomething() = 0;
   virtual void DoSomethingElse() = 0;
};

class Derived1
{
public:
   Derived1(){}
   virtual ~Derived1(){}
   virtual void DoSomething(){ ... }
   virtual void DoSomethingElse(){ ... }
   virtual void SpecialD1DoSomething{ ... }
};

class Derived2
{
public:
   Derived2(){}
   virtual ~Derived2(){}
   virtual void DoSomething(){ ... }
   virtual void DoSomethingElse(){ ... }
   virtual void SpecialD2DoSomething{ ... }
};

Je veux créer une instance de Derived1 ou Derived2 selon quelques réglages qui ne sont pas disponibles avant l'exécution.

Comme je ne peux pas déterminer le type dérivé jusqu'à l'exécution, alors pensez-vous que ce qui suit est une mauvaise pratique? ...

class X
{
public:
   ....

   void GetConfigurationValue()
   {
      ....
      // Get configuration setting, I need a "Derived1"
      b = new Derived1();

      // Now I want to call the special DoSomething for Derived1
      (dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();      
   }
private:
   Base* b;
};

J'ai lu généralement que l'utilisation de dynamic_cast est mauvais, mais comme je le disais, je ne sais pas type pour créer jusqu'à l'exécution. S'il vous plaît aider!

Était-ce utile?

La solution

L'utilisation dynamic_cast n'est pas une mauvaise pratique en soi. Il est une mauvaise pratique de l'utiliser de façon inappropriée, à savoir où il est pas vraiment nécessaire.

Il est aussi une mauvaise pratique de l'utiliser de cette façon:

(dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();  

Raison:. Dynamic_cast (b) peut renvoyer NULL

Lors de l'utilisation dynamic_cast, vous devez être très prudent, car il n'est pas garanti, que b est en fait de type Derived1 et non Derived2:

void GenericFunction(Base* p)
{
    (dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();
}

void InitiallyImplementedFunction()
{
   Derived1 d1;
   GenericFunction(&d1); // OK... But not for long. 
   // Especially, if implementation of GenericFunction is in another library
   // with not source code available to even see its implementation 
   // -- just headers
}    

void SomeOtherFunctionProbablyInAnotherUnitOfCompilation()
{
   Derived2 d2;
   GenericFunction(&d2); // oops!
}

Vous devez vérifier si dynamic_cast est en fait avec succès. Il y a deux façons de le faire: le vérifier avant et après la distribution. Avant la distribution, vous pouvez vérifier si le pointeur que vous essayez de cast est en fait celui que vous attendez via RTTI:

if (typeid(b) == typeid(Derived1*))
{
   // in this case it's safe to call the function right 
   // away without additional checks
   dynamic_cast<Derived1*>(b)->SpecialD1DoSomething();
}
else
{
  // do something else, like try to cast to Derived2 and then call
  // Derived2::SpecialD2DoSomething() in a similar fashion
}

Vérifier ce post-factum est en fait un peu plus simple:

Derived1* d1 = dynamic_cast<Derived1*>(b);
if (d1 != NULL)
{
   d1->SpecialD1DoSomething();
}

Je dirais aussi qu'il est une mauvaise pratique pour essayer de diminuer la frappe lors de la programmation en C ++. Il y a de nombreuses fonctionnalités en C ++ que semblent être tout à fait bien être tapé plus court (à savoir vous fait sentir »que NULL ne se produira jamais ici), mais se révèlent être une douleur dans le cul à déboguer après. ;)

Autres conseils

Pourquoi ne pas retarder le moment où vous « jeter » un peu si les informations de type en attribuant un pointeur dérivé à un pointeur à la base:

void GetConfigurationValue()
{
  // ...
  // Get configuration setting, I need a "Derived1"
  Derived1* d1 = new Derived1();
  b = d1;

  // Now I want to call the special DoSomething for Derived1
  d1->SpecialD1DoSomething();
}

Le point de fonctions virtuelles est qu'une fois que vous avez le bon type d'objet, vous pouvez appeler la fonction droite sans savoir quelle classe dérivée cet objet est - vous appelez simplement la fonction virtuelle, et il fait la bonne chose.

Vous avez seulement besoin d'un dynamic_cast lorsque vous avez une classe dérivée qui définit quelque chose de différent que de pas présent dans la classe de base, et vous avez besoin / envie de prendre quelque chose supplémentaire en compte.

Par exemple:

struct Base { 
    virtual void do_something() {}
};

struct Derived : Base { 
    virtual void do_something() {} // override dosomething
    virtual void do_something_else() {} // add a new function
};

Maintenant, si vous voulez juste appeler do_something(), un dynamic_cast est tout à fait inutile. Par exemple, vous pouvez avoir une collection de Base *, et juste Invoke do_something() sur chacun, sans prêter attention à savoir si l'objet est vraiment un Base ou Derived.

Quand / si vous avez un Base *, et que vous voulez appeler do_something_else(), puis vous pouvez utiliser un dynamic_cast pour savoir si l'objet lui-même est vraiment un Derived donc vous pouvez invoquer cela.

D'autres choses que vous pourriez envisager d'éviter l'utilisation de dynamic_cast

A partir de C ++ efficace (troisième édition) - Article 35 Alternatives aux fonctions virtuelles -

  1. 'pattern Template Method' via l'interface non-Vitual (INB). Faire les « emballage » fonctions virtuelles privé / protégé par une méthode publique - vous permet d'appliquer un autre flux de travail de choses à faire avant et après la méthode virtuelle.
  2. 'pattern stratégie' via des pointeurs de fonction. Passez dans la méthode supplémentaire en tant que pointeur de fonction.
  3. 'pattern Strategy' via TR1 :: fonction. semblable à 2. mais vous pouvez fournir des classes entières avec différentes options
  4. 'pattern Strategy' classique. Stratégie de la classe Separate principale - pousser les fonctions virtuelles dans une autre hiérarchie
  5. .

Il y a un modèle nommé modèle d'usine qui correspondrait à ce scénario. Cela vous permet de revenir une instance de la classe correcte en fonction de certains paramètres d'entrée.

Amusez-vous!

Quel est le problème:

Base * b;
if( some_condition ) {
   b = new Derived1;
}
else {
   b = new Derived2;
}

if ( Derived2 * d2 = dynamic_cast <Derived2 *>( b ) ) {
    d2->SpecialD2DoSomething();
}

Ou suis-je manque quelque chose?

Et peut-OI suggérer que lors de la publication des questions comme cela, vous (et d'autres) nommer vos classes A, B, C, etc., et vos fonctions choses comme f1 (), f2 (), etc. Il rend la vie beaucoup plus facile pour les personnes répondre à vos questions.

Une façon d'éviter dynamic_cast est d'avoir une fonction de trampoline virtuel « SpecialDoSomething » dont la mise en œuvre dérivée des appels polymorphes que « la SpecialDxDoSomething () » de particulier classe dérivée qui peut être ce nom de classe non-base que vous désirez. Il peut même appeler plus d'une fonction.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top