Les fonctions virtuelles non pures avec des paramètres sont-elles une mauvaise pratique?

StackOverflow https://stackoverflow.com/questions/260380

  •  06-07-2019
  •  | 
  •  

Question

J'ai une classe de base avec une fonction virtuelle facultative

class Base {
    virtual void OnlyImplementThisSometimes(int x) {}
};

Lorsque je compile ceci, un avertissement concernant le paramètre x non utilisé apparaît. Existe-t-il un autre moyen pour implémenter la fonction virtuelle? Je l'ai réécrit comme ceci:

class Base {
    virtual void OnlyImplementThisSometimes(int x) 
    {
        x = 0;
    }
};

J'ai également le problème suivant: si je ne fais pas attention, la sous-classe que je crée peut implémenter la mauvaise fonction, mais je ne le remarque pas à cause d'une surcharge: par exemple.

class Derived : public Base {
    void OnlyImplementThisSometimes(int x, int y) { // some code }
};

Derived d;
Base *b = dynamic_cast<Base *>(&d);
b->OnlyImplementThisSometimes(x); // calls the method in the base class

La méthode de la classe de base a été appelée car j'ai implémenté la fonction dérivée avec un " int y " param mais il n'y a pas d'avertissement à ce sujet. S'agit-il de pièges courants en C ++ ou ai-je mal compris les fonctions virtuelles?

Était-ce utile?

La solution

En ignorant les problèmes de conception, vous pouvez contourner l'avertissement du compilateur concernant une variable inutilisée en omettant le nom de la variable, par exemple:

virtual void OnlyImplementThisSometimes(int ) { }

Implémenter par erreur la signature de méthode incorrecte lors de la tentative de substitution de la fonction virtuelle est simplement une chose à laquelle vous devez faire attention en C ++. Des langues comme C # contournent cela avec le mot-clé 'override'.

Autres conseils

Nous définissons une macro _unused comme:

#define _unused(x) ((void)x)

Définissez ensuite la fonction comme suit:

virtual void OnlyImplementThisSometimes(int x) { _unused( x );}

Cela empêche non seulement le compilateur de se plaindre, mais il est également évident pour quiconque qui conserve le code que vous n'avez pas oublié de x - vous l'ignorez intentionnellement.

Pourquoi le définir dans la classe de base? Si la classe de base n'utilisera pas la méthode, définissez-la simplement en tant que méthode virtuelle dans votre classe dérivée.

Ou l'implémentation par défaut peut générer une exception

Si vous fournissez une implémentation par défaut d'une fonction virtuelle, il devrait s'agir d'une implémentation correcte pour toutes les classes dérivées qui ne remplacent pas cette fonction. Si vous ne pouvez pas fournir une implémentation correcte , mon conseil est de créer une fonction virtuelle pure et de laisser le soin à la classe dérivée de fournir une implémentation. Les classes dérivées qui ne permettent pas à la méthode d'être appelée peuvent générer une exception pour s'assurer qu'elle n'est pas utilisée par erreur.

En plus d’omettre simplement le nom de la variable, dans de nombreux compilateurs, vous pouvez indiquer au compilateur que vous savez qu’elle est inutilisée et SHUTUP en procédant ainsi

int func(int x)
{
   (void) x;
}

Ceci est assez courant dans mon code. Par exemple, j'ai des classes conçues pour des opérations mono-thread et multi-thread. Il y a beaucoup de routines et de données communes. Je mets tout cela dans la classe de base (qui a également quelques composants virtuels purs).

J'implémente deux fonctions virtuelles vides dans la classe de base: Init () et Cleanup (). La classe dérivée à un seul thread ne les implémente pas, mais la classe à plusieurs threads le fait.

J'ai une fonction fabrique qui crée la classe dérivée approprite puis retourne un pointeur. Le code client ne connaît que le type de classe de base et appelle Init () et Cleanup (). Les deux scénarios font le bon choix.

Bien sûr, il peut y avoir d’autres bonnes suggestions sur la façon de procéder, mais cet idiome fonctionne bien pour beaucoup de mon code.

Ce n'est pas une mauvaise pratique et c'est un idiome courant pour spécifier des parties d'une classe qui sont facultatives à implémenter.

Actuellement, je l'utilise pour un système de saisie utilisateur, car il serait fastidieux pour un utilisateur de cette classe d'implémenter chaque méthode même si elle ne l'utilisera probablement pas de toute façon.

class mouse_listener{
public:
    virtual ~mouse_listener() {}

    virtual void button_down(mouse_button a_Button) {}
    virtual void button_up(mouse_button a_Button) {}
    virtual void scroll_wheel(mouse_scroll a_Scroll) {}
    virtual void mouse_move_abs(math::point a_Position) {}
    virtual void mouse_move_rel(math::point a_Position) {}
};

Btw, si vous connaissez votre classe de base, vous n'avez jamais besoin de faire des diffusions up dynamiques, c'est-à-dire de dérivées à des bases.

Base *b = &d;

Fera aussi bien, dynamic_cast<> devrait plutôt être utilisé lors de la soustraction, c’est-à-dire de la base vers la version dérivée:

if((Derived *d = dynamic_cast<Derived *>(b)) != 0)
{
  // use d
}

(Et bien sûr, dans le cas d'un transfert, static_cast<> fonctionnera généralement aussi bien.)

Essayez ceci:

class Base {
    virtual void OnlyImplementThisSometimes(int x) = 0;
};

Cela fait longtemps que je n’ai pas fait ce genre de choses, mais je pense que c’est ainsi que vous déclarez une fonction virtuelle.

Comme d'autres l'ont déjà dit, les noms de variable sont facultatifs dans les déclarations de fonction comme celle-ci.

La réponse la plus simple à cette question est indiquée ci-dessous:

class Base {
    virtual void OnlyImplementThisSometimes(int x) { x;}
};

Une simple référence à la variable qui ne fait absolument rien ne supprimera tous les avertissements (de toute façon de VC ++ au plus haut niveau).

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