web-dev-qa-db-fra.com

Remplacer en toute sécurité les fonctions virtuelles C ++

J'ai une classe de base avec une fonction virtuelle et je veux remplacer cette fonction dans une classe dérivée. Existe-t-il un moyen de faire vérifier par le compilateur si la fonction que j'ai déclarée dans la classe dérivée remplace une fonction de la classe de base? Je voudrais ajouter une macro ou quelque chose qui assure que je n'ai pas accidentellement déclaré une nouvelle fonction, au lieu de remplacer l'ancienne.

Prenons cet exemple:

class parent {
public:
  virtual void handle_event(int something) const {
    // boring default code
  }
};

class child : public parent {
public:
  virtual void handle_event(int something) {
    // new exciting code
  }
};

int main() {
  parent *p = new child();
  p->handle_event(1);
}

Ici, parent::handle_event() est appelée à la place de child::handle_event(), car la méthode de l'enfant manque la déclaration const et déclare donc une nouvelle méthode. Cela pourrait également être une faute de frappe dans le nom de la fonction ou une différence mineure dans les types de paramètres. Cela peut également se produire facilement si l'interface de la classe de base change et si une classe dérivée n'a pas été mise à jour pour refléter le changement.

Existe-t-il un moyen d'éviter ce problème, puis-je demander au compilateur ou à un autre outil de vérifier cela pour moi? Des indicateurs utiles du compilateur (de préférence pour g ++)? Comment évitez-vous ces problèmes?

98
sth

Depuis g ++ 4.7, il comprend le nouveau mot clé C++ 11 override:

class child : public parent {
    public:
      // force handle_event to override a existing function in parent
      // error out if the function with the correct signature does not exist
      void handle_event(int something) override;
};
85
Gunther Piez

Quelque chose comme le mot clé override de C # ne fait pas partie de C++.

En gcc, -Woverloaded-virtual prévient de ne pas masquer une fonction virtuelle de classe de base avec une fonction du même nom mais une signature suffisamment différente pour ne pas la remplacer. Cela ne vous protégera cependant pas contre le non-remplacement d'une fonction en raison d'une mauvaise orthographe du nom de la fonction elle-même.

20
CB Bailey

Autant que je sache, ne pouvez-vous pas le résumer?

class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

Je pensais avoir lu sur www.parashift.com que vous pouviez réellement implémenter une méthode abstraite. Ce qui est logique pour moi personnellement, la seule chose qu’il fait est de forcer les sous-classes à l’appliquer, personne n’a rien dit sur le fait qu’il n’était pas autorisé à avoir une implémentation elle-même.

17
Ray Hidayat

En MSVC, vous pouvez utiliser le mot clé CLR override même si vous ne compilez pas pour CLR.

En g ++, il n’existe aucun moyen direct d’appliquer cela dans tous les cas; d'autres personnes ont donné de bonnes réponses sur la manière de corriger les différences de signature en utilisant -Woverloaded-virtual. Dans une version ultérieure, quelqu'un pourrait ajouter une syntaxe telle que __attribute__ ((override)) ou l'équivalent à l'aide de la syntaxe C++ 0x.

11
Doug

En MSVC++, vous pouvez utiliser mot clé override

class child : public parent {
public:
  virtual void handle_event(int something) <b>override</b> {
    // new exciting code
  }
};

override fonctionne à la fois pour le code natif et le code CLR dans MSVC++.

9
bobobobo

Faites en sorte que la fonction soit abstraite afin que les classes dérivées n’aient pas d’autre choix que de la remplacer.

@Ray Votre code est invalide.

class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

Les fonctions abstraites ne peuvent pas avoir de corps défini en ligne. Il doit être modifié pour devenir

class parent {
public:
  virtual void handle_event(int something) const = 0;
};

void parent::handle_event( int something ) { /* do w/e you want here. */ }
5
Tanveer Badar

Je suggérerais un léger changement dans votre logique. Cela peut ou peut ne pas fonctionner, selon ce que vous devez accomplir.

handle_event () peut toujours faire le "code par défaut ennuyeux" mais au lieu d'être virtuel, au moment où vous voulez qu'il fasse le "nouveau code excitant", la classe de base appelle une méthode abstraite (c'est-à-dire qu'elle doit être remplacée) qui sera fourni par votre classe de descendants.

EDIT: Et si vous décidez plus tard que certaines de vos classes descendantes pas doivent fournir "un nouveau code excitant", vous pouvez changer le résumé en virtuel et fournir une implémentation de classe de base vide de "inséré" fonctionnalité.

3
JMD

Votre compilateur peut avoir un avertissement qu’il peut générer si une fonction de classe de base est masquée. Si c'est le cas, activez-le. Cela attrapera les conflits de const et les différences dans les listes de paramètres. Malheureusement, cela ne permettra pas de découvrir une faute d'orthographe.

Par exemple, il s’agit de l’avertissement C4263 dans Microsoft Visual C++.

2
Mark Ransom