web-dev-qa-db-fra.com

Virtuel / virtuel virtuel expliqué

Qu'est-ce que cela signifie exactement si une fonction est définie comme virtuelle et est-ce la même chose que du virtuel pur?

320
Justin

De fonction virtuelle de Wikipedia ...

Dans la programmation orientée objet, dans des langages tels que C++ et Object Pascal, une fonction virtuelle ou une méthode virtuelle est une fonction ou une méthode pouvant être héritée et remplaçable pour laquelle la répartition dynamique est facilitée. Ce concept est une partie importante de la partie polymorphisme (à l'exécution) de la programmation orientée objet (OOP). En bref, une fonction virtuelle définit une fonction cible à exécuter, mais la cible peut ne pas être connue au moment de la compilation.

Contrairement à une fonction non virtuelle, lorsqu'une fonction virtuelle est remplacée, la version la plus dérivée est utilisée à tous les niveaux de la hiérarchie de classes, et pas uniquement au niveau auquel elle a été créée. Par conséquent, si une méthode de la classe de base appelle une méthode virtuelle, la version définie dans la classe dérivée sera utilisée à la place de la version définie dans la classe de base. .

Cela contraste avec les fonctions non virtuelles, qui peuvent toujours être remplacées dans une classe dérivée, mais la "nouvelle" version ne sera utilisée que par la classe dérivée et au-dessous, mais ne modifiera pas du tout la fonctionnalité de la classe de base.

tandis que..

Une fonction virtuelle pure ou une méthode virtuelle pure est une fonction virtuelle qui doit être implémentée par une classe dérivée si la classe dérivée n'est pas abstraite.

Quand une méthode virtuelle pure existe, la classe est "abstraite" et ne peut être instanciée par elle-même. Au lieu de cela, une classe dérivée qui implémente la ou les méthodes virtuelles pures doit être utilisée. Un pur virtuel n'est pas du tout défini dans la classe de base, donc une classe dérivée doit le définir, ou cette classe dérivée est aussi abstraite, et ne peut pas être instancié. Seule une classe qui n'a pas de méthodes abstraites peut être instanciée.

Un virtuel fournit un moyen de remplacer les fonctionnalités de la classe de base, et un virtuel pur le requiert .

311
Diego Dias

Je voudrais commenter la définition du mot virtuel de Wikipedia, telle que répétée par plusieurs ici. [Au moment où cette réponse a été écrite,] Wikipedia a défini une méthode virtuelle comme pouvant être remplacée par des sous-classes. [Heureusement, Wikipedia a été modifié depuis et l'explique maintenant correctement.] C'est incorrect: n'importe quelle méthode, pas seulement virtuelle, peut être remplacée dans des sous-classes. Ce que le virtuel fait, c'est vous donner le polymorphisme, c'est-à-dire le possibilité de sélectionner au moment de l'exécution le remplacement le plus dérivé d'une méthode.

Considérons le code suivant:

#include <iostream>
using namespace std;

class Base {
public:
    void NonVirtual() {
        cout << "Base NonVirtual called.\n";
    }
    virtual void Virtual() {
        cout << "Base Virtual called.\n";
    }
};
class Derived : public Base {
public:
    void NonVirtual() {
        cout << "Derived NonVirtual called.\n";
    }
    void Virtual() {
        cout << "Derived Virtual called.\n";
    }
};

int main() {
    Base* bBase = new Base();
    Base* bDerived = new Derived();

    bBase->NonVirtual();
    bBase->Virtual();
    bDerived->NonVirtual();
    bDerived->Virtual();
}

Quel est le résultat de ce programme?

Base NonVirtual called.
Base Virtual called.
Base NonVirtual called.
Derived Virtual called.

Derived remplace toutes les méthodes de Base: non seulement la méthode virtuelle, mais également la méthode non virtuelle.

Nous voyons que lorsque vous avez un pointeur de base sur Derived (bDerived), l'appel de NonVirtual appelle l'implémentation de la classe Base. Ceci est résolu au moment de la compilation: le compilateur voit que bDerived est une base *, que NonVirtual n'est pas virtuel, il effectue donc la résolution sur la classe Base.

Cependant, l'appel de Virtual appelle l'implémentation de la classe Derived. En raison du mot clé virtual, la sélection de la méthode a lieu à au moment de l'exécution , pas à la compilation. Au moment de la compilation, ce qui se passe ici, c'est que le compilateur voit qu'il s'agit d'une Base * et qu'il appelle une méthode virtuelle. Il insère donc un appel à vtable au lieu de la classe Base. Cette vtable est instanciée au moment de l'exécution, d'où la résolution de l'exécution au remplacement le plus dérivé.

J'espère que ce n'était pas trop déroutant. En bref, toute méthode peut être remplacée, mais seules les méthodes virtuelles vous confèrent un polymorphisme, c'est-à-dire une sélection au moment de l'exécution du remplacement le plus dérivé. Cependant, en pratique, le fait de remplacer une méthode non virtuelle est considéré comme une mauvaise pratique et est rarement utilisé. Par conséquent, beaucoup de personnes (y compris celui qui a écrit cet article de Wikipedia) pensent que seules les méthodes virtuelles peuvent être remplacées.

197
Asik

Le mot clé virtual donne à C++ la possibilité de prendre en charge le polymorphisme. Lorsque vous avez un pointeur sur un objet d'une classe telle que:

class Animal
{
  public:
    virtual int GetNumberOfLegs() = 0;
};

class Duck : public Animal
{
  public:
     int GetNumberOfLegs() { return 2; }
};

class Horse : public Animal
{
  public:
     int GetNumberOfLegs() { return 4; }
};

void SomeFunction(Animal * pAnimal)
{
  cout << pAnimal->GetNumberOfLegs();
}

Dans cet exemple (idiot), la fonction GetNumberOfLegs () renvoie le nombre approprié en fonction de la classe de l'objet pour lequel elle est appelée.

Maintenant, considérons la fonction 'SomeFunction'. Peu importe le type d'objet animal qui lui est transmis, à condition qu'il soit dérivé d'Animal. Le compilateur convertit automatiquement toute classe dérivée d'Animal en un animal, car il s'agit d'une classe de base.

Si nous faisons cela:

Duck d;
SomeFunction(&d);

il aurait sorti '2'. Si nous faisons cela:

Horse h;
SomeFunction(&h);

il aurait sorti '4'. Nous ne pouvons pas faire ceci:

Animal a;
SomeFunction(&a);

car il ne sera pas compilé car la fonction virtuelle GetNumberOfLegs () est pure, ce qui signifie qu'elle doit être implémentée en dérivant des classes (sous-classes).

Les fonctions virtuelles pures servent principalement à définir:

a) classes abstraites

Ce sont des classes de base où vous devez en dériver puis implémenter les fonctions virtuelles pures.

b) interfaces

Ce sont des classes "vides" où toutes les fonctions sont virtuelles et donc vous devez dériver puis implémenter toutes les fonctions.

112
JBRWilkinson

Dans une classe C++, virtual est le mot clé qui désigne le fait qu'une méthode peut être remplacée (c'est-à-dire implémentée par) une sous-classe. Par exemple:

class Shape 
{
  public:
    Shape();
    virtual ~Shape();

    std::string getName() // not overridable
    {
      return m_name;
    }

    void setName( const std::string& name ) // not overridable
    {
      m_name = name;
    }

  protected:
    virtual void initShape() // overridable
    {
      setName("Generic Shape");
    }

  private:
    std::string m_name;
};

Dans ce cas, une sous-classe peut remplacer la fonction initShape pour effectuer un travail spécialisé:

class Square : public Shape
{
  public: 
    Square();
    virtual ~Square();

  protected:
    virtual void initShape() // override the Shape::initShape function
    {
      setName("Square");
    }
}

Le terme pure virtual fait référence aux fonctions virtuelles devant être implémentées par une sous-classe et n'ayant pas été implémentées par la classe de base. Vous désignez une méthode comme virtuelle pure en utilisant le mot clé virtual et en ajoutant un = à la fin de la déclaration de la méthode.

Donc, si vous voulez rendre Shape :: initShape pur virtuel, procédez comme suit:

class Shape 
{
 ...
    virtual void initShape() = 0; // pure virtual method
 ... 
};

En ajoutant une méthode virtuelle pure à votre classe, vous faites de la classe un classe de base abstraite , ce qui est très pratique pour séparer les interfaces de l'implémentation.

30
Nick Haddad

"Virtuel" signifie que la méthode peut être remplacée dans des sous-classes, mais qu'elle a une implémentation directement appelable dans la classe de base. "Virtuel pur" signifie qu'il s'agit d'une méthode virtuelle sans implémentation directement appelable. Une telle méthode doit doit être remplacée au moins une fois dans la hiérarchie d'héritage - si une classe a des méthodes virtuelles non implémentées, les objets de cette classe ne peuvent pas être construits et la compilation échouera.

@quark souligne que les méthodes purement virtuelles can ont une implémentation, mais comme les méthodes purement virtuelles doivent être remplacées, l'implémentation par défaut ne peut pas être appelée directement. Voici un exemple de méthode virtuelle pure avec une valeur par défaut:

#include <cstdio>

class A {
public:
    virtual void Hello() = 0;
};

void A::Hello() {
    printf("A::Hello\n");
}

class B : public A {
public:
    void Hello() {
        printf("B::Hello\n");
        A::Hello();
    }
};

int main() {
    /* Prints:
           B::Hello
           A::Hello
    */
    B b;
    b.Hello();
    return 0;
}

Selon les commentaires, le fait que la compilation échoue ou non dépend du compilateur. Dans GCC 4.3.3 au moins, il ne compilera pas:

class A {
public:
    virtual void Hello() = 0;
};

int main()
{
    A a;
    return 0;
}

Sortie:

$ g++ -c virt.cpp 
virt.cpp: In function ‘int main()’:
virt.cpp:8: error: cannot declare variable ‘a’ to be of abstract type ‘A’
virt.cpp:1: note:   because the following virtual functions are pure within ‘A’:
virt.cpp:3: note:   virtual void A::Hello()
15
John Millikin

Comment fonctionne le mot clé virtuel?

Supposons que l’homme soit une classe de base, l’indien étant dérivé de l’homme.

Class Man
{
 public: 
   virtual void do_work()
   {}
}

Class Indian : public Man
{
 public: 
   void do_work()
   {}
}

Déclarer do_work () en tant que virtuel signifie simplement: l'appel de do_work () sera déterminé UNIQUEMENT au moment de l'exécution.

Supposons que je fais,

Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.

Si virtual n'est pas utilisé, celui-ci est déterminé ou lié statiquement par le compilateur, en fonction de l'objet appelé. Donc, si un objet de Man appelle do_work (), do_work () de l'homme s'appelle MÊME SI IL POINTE SUR UN OBJET INDIEN

Je crois que la réponse la plus votée est trompeuse - Toute méthode, qu'elle soit virtuelle ou non, peut avoir une implémentation ignorée dans la classe dérivée. Avec une référence spécifique à C++, la différence correcte est l'exécution (lorsque virtual est utilisé), la liaison et la compilation (lorsque virtual n'est pas utilisé mais qu'une méthode est remplacée et qu'un pointeur de base est pointé sur un objet dérivé), une liaison des fonctions associées.

Il semble y avoir un autre commentaire trompeur qui dit,

"Justin," virtuel pur "est juste un terme (pas un mot clé, voir ma réponse ci-dessous) utilisé pour signifier" cette fonction ne peut pas être implémentée par la classe de base ".

CECI IS FAUX! Les fonctions purement virtuelles peuvent également avoir un corps ET PEUVENT ÊTRE MISES EN PLACE! La vérité est que la fonction virtuelle pure d'une classe abstraite peut être appelée statiquement! Deux très bons auteurs sont Bjarne Stroustrup et Stan Lippman ... parce qu'ils ont écrit la langue.

9
McMurdo

Simula, C++ et C #, qui utilisent la liaison de méthode statique par défaut, le programmeur peut spécifier que des méthodes particulières doivent utiliser la liaison dynamique en les étiquetant comme virtuelles. La liaison de méthode dynamique est essentielle à la programmation orientée objet.

La programmation orientée objet requiert trois concepts fondamentaux: l'encapsulation, l'héritage et la liaison de méthode dynamique.

Encapsulation permet de masquer les détails d'implémentation d'une abstraction derrière une interface simple.

Héritage permet de définir une nouvelle abstraction comme une extension ou un raffinement d'une abstraction existante, obtenant automatiquement certaines ou toutes ses caractéristiques.

Liaison de méthode dynamique permet à la nouvelle abstraction d'afficher son nouveau comportement même lorsqu'elle est utilisée dans un contexte qui attend l'ancienne abstraction.

2
PJT

Les méthodes virtuelles peuvent être remplacées par des classes dérivées, mais nécessitent une implémentation dans la classe de base (celle qui sera remplacée)

Les méthodes virtuelles pures n'ont pas d'implémentation de la classe de base. Ils doivent être définis par des classes dérivées. (Donc, techniquement, remplacer n'est pas le bon terme, car il n'y a rien à remplacer).

Virtual correspond au comportement par défaut Java lorsque la classe dérivée remplace une méthode de la classe de base.

Les méthodes virtuelles pures correspondent au comportement des méthodes abstraites au sein de classes abstraites. Et une classe qui ne contiendrait que des méthodes et des constantes virtuelles pures serait le pendant-cpp d'une interface.

1
johannes_lalala

ne fonction virtuelle est une fonction membre déclarée dans une classe de base et redéfinie par une classe dérivée. Les fonctions virtuelles sont hiérarchiques par ordre d'héritage. Lorsqu'une classe dérivée ne remplace pas une fonction virtuelle, la fonction définie dans sa classe de base est utilisée.

ne fonction virtuelle pure est une fonction qui ne contient aucune définition relative à la classe de base. Elle n'a aucune implémentation dans la classe de base. Toute classe dérivée doit remplacer cette fonction.

0
rashedcs

Fonction virtuelle pure

essayez ce code

#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{

public:

    virtual void sayHellow()=0;

};

class anotherClass:aClassWithPureVirtualFunction
{

public:

    void sayHellow()
    {

        cout<<"hellow World";
    }

};
int main()
{
    //aClassWithPureVirtualFunction virtualObject;
    /*
     This not possible to create object of a class that contain pure virtual function
    */
    anotherClass object;
    object.sayHellow();
}

Dans la classe anotherClass, supprimez la fonction sayHellow et exécutez le code. vous obtiendrez une erreur! Parce qu’une classe contenant une fonction virtuelle pure, aucun objet ne peut être créé à partir de cette classe et qu’elle est héritée, sa classe dérivée doit implémenter cette fonction.

Fonction virtuelle

essayez un autre code

#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{

public:

    virtual void sayHellow()
    {
        cout<<"from base\n";
    }

};

class anotherClass:public aClassWithPureVirtualFunction
{

public:

    void sayHellow()
    {

        cout<<"from derived \n";
    }

};
int main()
{
    aClassWithPureVirtualFunction *baseObject=new aClassWithPureVirtualFunction;
    baseObject->sayHellow();///call base one

    baseObject=new anotherClass;
    baseObject->sayHellow();////call the derived one!

}

Ici, la fonction sayHellow est marquée comme virtuelle dans la classe de base.Elle dit le compilateur qui cherche la fonction dans la classe dérivée et implémente la fonction.Si non trouvé, exécutez celui de base.Merci

0

"Une fonction virtuelle ou une méthode virtuelle est une fonction ou une méthode dont le comportement peut être remplacé dans une classe héritée par une fonction avec la même signature" - wikipedia

Ce n'est pas une bonne explication pour les fonctions virtuelles. Car, même si un membre n'est pas virtuel, les classes héritées peuvent le remplacer. Vous pouvez essayer de le voir vous-même.

La différence se manifeste lorsqu'une fonction prend une classe de base en tant que paramètre. Lorsque vous spécifiez une classe héritante en entrée, cette fonction utilise l'implémentation de la classe de base de la fonction remplacée. Toutefois, si cette fonction est virtuelle, elle utilise celle implémentée dans la classe dérivée.

0
can
  • Les fonctions virtuelles doivent avoir une définition dans la classe de base et également dans la classe dérivée, mais pas nécessairement. Par exemple, la fonction ToString () ou toString () est virtuelle. Vous pouvez donc fournir votre propre implémentation en la remplaçant dans la ou les classes définies par l'utilisateur.

  • Les fonctions virtuelles sont déclarées et définies en classe normale.

  • La fonction virtuelle pure doit être déclarée se terminant par "= 0" et ne peut être déclarée qu'en classe abstraite.

  • Une classe abstraite ayant une ou plusieurs fonctions virtuelles pures ne peut pas avoir de définition de ces fonctions virtuelles pures; elle implique donc que l'implémentation doit être fournie dans les classes dérivées de cette classe abstraite.

0
Sohail xIN3N