2009-12-16 5 views
7

J'ai récemment eu un problème frustrant qui s'est résumée à une erreur de codage très simple. Considérez le code suivant:Avertissement pour un mot clé virtuel manquant

#include <iostream> 

class Base 
{ 
public: 
    void func() { std::cout << "BASE" << std::endl; } 
}; 

class Derived : public Base 
{ 
public: 
    virtual void func() { std::cout << "DERIVED" << std::endl; } 
}; 

int main(int argc, char* argv[]) 
{ 
    Base* obj = new Derived; 
    obj->func(); 
    delete obj; 

    return 0; 
} 

La sortie est

BASE

Il est évident que (pour ce cas), je voulais mettre le mot-clé virtuel sur la base :: func afin que Derived :: func serait appelé en main. Je me rends compte que c'est (probablement) permis par la norme C++, et peut-être avec raison, mais il me semble que 99% du temps ce serait une erreur de codage. Cependant, quand j'ai compilé en utilisant g ++ et toutes les options -Wblah auxquelles je pouvais penser, aucun avertissement n'a été généré.

Y at-il un moyen de générer un avertissement quand une classe de base et une classe dérivée ont des fonctions membres du même nom où la fonction de la classe dérivée est virtuelle et la fonction de la classe de base n'est pas?

+0

'delete' dans l'exemple ne peut pas détruire' 'Derived' parce base' n'a pas de' virtual' destructor –

Répondre

5

En Visual C++, vous pouvez utiliser l'extension override. Comme ceci:

virtual void func() override { std::cout << "DERIVED" << std::endl; } 

Ceci donnera une erreur si la fonction ne remplace pas réellement une méthode de classe de base. Je l'utilise pour TOUTES les fonctions virtuelles. En général, je définir une macro comme ceci:

#ifdef _MSC_VER 
#define OVERRIDE override 
#else 
#define OVERRIDE 
#endif 

donc je peux l'utiliser comme ceci:

virtual void func() OVERRIDE { std::cout << "DERIVED" << std::endl; } 

J'ai cherché quelque chose comme ça en g ++ mais ne pouvait pas trouver un concept similaire. La seule chose que je n'aime pas dans Visual C++ est que vous ne pouvez pas avoir besoin du compilateur (ou au moins warn) sur toutes les fonctions surchargées.

+0

Je l'utilise aussi. C'est également pratique lorsque quelqu'un change la signature d'une fonction (ajout d'un paramètre, etc.) mais ne parvient pas à mettre à jour la ou les contreparties de base ou dérivées. Normalement, la virtualité cesserait silencieusement de fonctionner, mais cette astuce oblige le compilateur à générer une erreur. –

+1

Super! Je ne savais pas à propos de cette extension. Je vais probablement l'utiliser pour mes propres projets depuis que j'utilise Visual Studio, mais pour ce projet particulier, je vais devoir coller à g ++ :( – Jonesinator

+2

s'il vous plaît noter que 'override' est en C++ 11, de sorte que cela fonctionnera avec gcc et clang récents en mode C++ 11 ('-std = C++ 11') –

3

Je ne connais pas d'indicateur g ++ pour produire un avertissement à ce sujet (pour ne pas dire qu'il n'y en a pas), mais je dirais que c'est une erreur assez rare. La plupart des gens écrivent d'abord la classe de base, en tant qu'interface utilisant des fonctions virtuelles pures. Si vous aviez dit:

void func() = 0; 

alors vous obtiendrez une erreur de syntaxe.

1

homme gcc

-Woverloaded virtuel (C++ et Objective-C++ uniquement) Avertir lorsqu'une déclaration de fonction cache des fonctions virtuelles à partir d'une classe de base. Par exemple, dans:

  struct A { 
      virtual void f(); 
      }; 

      struct B: public A { 
      void f(int); 
      }; 

    the "A" class version of "f" is hidden in "B", and code like: 

      B* b; 
      b->f(); 

    will fail to compile. 
+0

L'utilisation de cette option ne prévient pas dans le code affiché ci-dessus.Dans ce cas, Foo :: func n'est pas virtuel (ce qui est un pré-requis de l'avertissement), et même s'il l'était, il ne déclencherait toujours pas l'avertissement car Bar :: func correspond à la signature correcte. Si j'ai fait Foo :: func virtual ET changé la signature de Bar :: func (c'est-à-dire func (int)) alors seulement le déclenchement de l'avertissement. Dans mon cas, je remplace une fonction NON-virtuelle de Foo, ce qui signifie que si j'ai un Foo *, alors l'implémentation de Foo sera appelée, et non l'implémentation de la barre. Cependant, seulement si j'ai une barre *, l'implémentation Bar sera appelée. – Jonesinator