Question

Je suis nouveau à C ++.

Quelqu'un pourrait me dire la différence entre la méthode et primordiale concepts de fonctions virtuelles en c ++.

La fonctionnalité des fonctions virtuelles peut être supplantée dans ses classes dérivées. Redéfinir une fonction dans une classe dérivée est appelée fonction primordiale.

pourquoi nous avons effectivement des fonctions virtuelles?

Était-ce utile?

La solution

Résumé

Dans cet article, nous discutons des fonctions virtuelles en C ++. Partie zéro explique comment les fonctions virtuelles sont déclarées et réécrite. Première partie tentatives (et ne parvient peut-être) pour expliquer comment les fonctions virtuelles sont mises en œuvre. La deuxième partie est un exemple de programme qui utilise les classes d'exemple définies dans les parties zéro et un. La troisième partie est l'exemple animal classique donné dans chaque fonction virtuelle -. Tutoriel polymorphisme

PARTIE ZERO

Une méthode d'une classe est dite virtuel si et seulement si la déclaration de l'être.

class my_base
{
public:
            void non_virtual_test() { cout << 4 << endl; } // non-virtual
    virtual void virtual_test()     { cout << 5 << endl; } // virtual
};

(Bien sûr, je suppose que le programmeur n'a pas fait auparavant quelque chose comme #define virtual.)

Une classe qui redéclare et re-met en oeuvre une méthode non virtuelle de l'une de ses bases est dit que la méthode de surcharge . Une classe qui redéclare et re-met en oeuvre une méthode virtuelle de l'une de ses bases est dite override cette méthode.

class my_derived : public my_base
{
public:
    void non_virtual_test() { cout << 6 << endl; } // overloaded
    void virtual_test()     { cout << 7 << endl; } // overriden
};

PARTIE

Lorsque le compilateur détecte une classe a des méthodes virtuelles, il ajoute automatiquement une table de méthode virtuelle (également connu sous le nom vtable ) à la classe mise en page de mémoire. Le résultat est similaire à ce qui aurait été généré à partir de la compilation de ce code:

class my_base
{
//<vtable>
// The vtable is actually a bunch of member function pointers
protected:
    void (my_base::*virtual_test_ptr)();
//</vtable>

// The actual implementation of the virtual function
// is hidden from the rest of the program.
private:
    void virtual_test_impl() { cout << 5 << endl; }

// Initializing the real_virtual_test pointer in the vtable.
public:
    my_base() : virtual_test_ptr(&my_base::virtual_test_impl) {}

public:
    void non_virtual_test() { cout << 4 << endl; }
    // The interface of the virtual function is a wrapper
    // around the member function pointer.
    inline void virtual_test() { *virtual_test_ptr(); }
};

Lorsque le compilateur détecte une classe a une méthode virtuelle substituée, elle remplace son entrée associée dans la vtable. Le résultat est similaire à ce qui aurait été généré à partir de la compilation de ce code:

class my_derived : public my_base
{
// The actual implementation of the virtual function
// is hidden from the rest of the program.
private:
    void virtual_test_impl() { cout << 7 << endl; }

// Initializing the real_virtual_test pointer in the vtable.
public:
    my_derived() : virtual_test_ptr(&my_derived::virtual_test_impl) {}

public:
    void non_virtual_test() { cout << 6 << endl; }
};

DEUXIÈME PARTIE

Maintenant qu'il est clair que les fonctions virtuelles sont mises en œuvre à l'aide vtables, qui ne sont rien d'autre qu'un tas de pointeurs de fonction, il doit être clair que ce code fait:

#include <iostream>

using namespace std;

class my_base
{
    public:
            void non_virtual_test() { cout << 4 << endl; }
    virtual void virtual_test()     { cout << 5 << endl; }
};

class my_derived : public my_base
{
public:
    void non_virtual_test() { cout << 6 << endl; }
    void virtual_test()     { cout << 7 << endl; }
}

int main()
{
    my_base* base_obj = new my_derived();

    // This outputs 4, since my_base::non_virtual_test() gets called,
    // not my_derived::non_virtual_test().
    base_obj->non_virtual_test();

    // This outputs 7, since the vtable pointer points to
    // my_derived::virtual_test(), not to my_base::virtual_test().
    base_obj->virtual_test();

    // We shall not forget
    // there was an object that was pointed by base_obj
    // who happily lived in the heap
    // until we killed it.
    delete base_obj;

    return 0;
}

TROISIÈME PARTIE

Étant donné qu'aucun exemple de fonction virtuelle est complète sans un exemple avec des animaux ...

#include <iostream>

using namespace std;

class animal
{
public:
    virtual void say_something()
    { cout << "I don't know what to say." << endl
           << "Let's assume I can growl." << endl; }

    /* A more sophisticated version would use pure virtual functions:
     *
     * virtual void say_something() = 0;
     */
};

class dog : public animal
{
public:
    void say_something() { cout << "Barf, barf..." << endl; }
};

class cat : public animal
{
public:
    void say_something() { cout << "Meow, meow..." << endl; }
};

int main()
{
    animal *a1 = new dog();
    animal *a2 = new cat();
    a1->say_something();
    a2->say_something();
}

Autres conseils

fonction virtuelle / méthode est simplement une fonction dont le comportement peut être surchargé dans une sous-classe (ou en C ++ termes d'une classe dérivée) en redéfinissant la façon dont fonctionne la fonction (en utilisant la même signature).

Pensez à un mammifère classe de base avec une fonction de parler. La fonction est nulle et Couts simplement comment un mammifère parle. Lorsque vous héritez de cette classe, vous pouvez remplacer la méthode de parler afin que les chiens vont « Arf Arf! » et les chats vont "Meow Meow".

Votre question semble demander quelles sont les différences, bien il n'y en a pas, car avec des fonctions virtuelles, on peut remplacer le comportement de ces fonctions. Vous pouvez être après la différence entre les fonctions et les impérieuses surcharge.

La surcharge des fonctions des moyens pour créer une fonction du même nom, mais différents arguments à-dire différent en nombre et type d'argument (s). Voici une explication sur la surcharge en C ++ site IBM :

  

Surcharger (C ++ uniquement) Si vous spécifiez plus d'une définition pour une   nom de la fonction ou un opérateur dans la même portée, vous avez surchargé   que le nom de la fonction ou l'opérateur. les fonctions et les opérateurs sont surchargées   décrit dans les fonctions Surcharge (C ++ uniquement) et de surcharger   opérateurs (C ++ uniquement), respectivement.

     

Une déclaration surchargée est une déclaration qui avait été déclarée avec   le même nom que la déclaration précédemment déclarée dans la même portée,   sauf que les deux déclarations ont différents types.

     

Si vous appelez un nom de fonction surchargée ou l'opérateur, le compilateur   détermine la définition la plus appropriée à utiliser en comparant la   types d'arguments que vous avez utilisé pour appeler la fonction ou l'opérateur avec le   types de paramètres spécifiés dans les définitions. Le processus de sélection   la fonction surcharge la plus appropriée ou l'opérateur est appelé   la résolution de surcharge, tel que décrit dans la résolution de surcharge (C ++ uniquement).

Quant à la raison rationnelle pour les situations pleine où les fonctions virtuelles sont nécessaires, ce blog donne un bon: http://nrecursions.blogspot.in/2015/06/so-why-do-we-need-virtual-functions.html

La différence entre la fonction et primordiale fonction virtual devient importante avec polymorphisme . Plus précisément lors de l'utilisation des références ou des pointeurs vers une classe de base.

La configuration de base

C ++, toute classe dérivée peut être transmis à une fonction exigeant un objet de classe de base. (Voir aussi Slicing et LSP ). Compte tenu:

struct Base_Virtual
{
  virtual void some_virtual_function();
};

struct Base_Nonvirtual
{
  void some_function();
};

void Function_A(Base_Virtual * p_virtual_base);
void Function_B(Base_Nonvirtual * p_non_virtual_base);

Dans le code ci-dessus, il existe deux classes de base, on déclare une méthode virtuelle, l'autre déclare une fonction non virtuelle.

Deux fonctions sont déclarées qui nécessitent des pointeurs vers les clases de base respectives.

Les classes dérivées

Testons maintenant le polymorphisme, en particulier par rapport virtual (méthodes prépondérants) non virtuels. Les structures:

struct Derived_From_Virtual
: public Base_Virtual
{
  void some_virtual_function(); // overrides Base_Virtual::some_virtual_function()
};

struct Derived_From_Nonvirtual : Base_Nonvirtual publique {   une_fonction vide (); }

Selon le langage C ++, je peux passer un pointeur vers un Derived_From_Virtual à Function_A parce Derived_From_Virtual hérite de Base_Virtual. Je peux aussi passer un pointeur vers Derived_From_Nonvirtual à Function_B.

La différence entre virtual et en remplaçant

Le modificateur de virtual dans Base_Virtual, indique au compilateur que Function_A utilisera Derived_From_Virtual::some_virtual_function() au lieu de la méthode Base_Virtual. En effet, la méthode est virtuel , la définition finale peut résider dans un futur ou dérivés classe. La définition actuelle dit d'utiliser la méthode dans la classe la plus dérivée contenant la définition.

Lors du passage d'un pointeur vers Derived_From_Nonvirtual à Function_B, le compilateur charger la fonction pour utiliser la méthode de la classe de base, Base_Nonvirtual::some_function(). La méthode de some_function() dans la classe dérivée est séparé, indépendant, procédé de la classe de base.

La principale différence entre virtual et dominante se produit avec le polymorphisme.

Consultez C ++ FAQ Lite, http://www.parashift.com/c++-faq -lite / . est probablement l'une des meilleures ressources ++ C pour les débutants. il a en profondeur écriture environ des fonctions virtuelles et dominante.

Personnellement, je trouve C ++ FAQ pour être une excellente source que j'apprends C ++. D'autres personnes ont autre opinion, votre kilométrage peut varier

Ceci est plus un suivi des commentaires de cette répondre qu'une réponse lui-même.

virtual est un mot clé qui demande l'envoi d'exécution de la méthode étant déclarée et en même temps déclare la méthode comme l'un des overrides (mis en œuvre des méthodes virtuelles pures de côté). La méthode déclarée, et toute méthode qui partage la signature et le nom exact dans la hiérarchie dérivateur de cette classe sont en baisse overrides . Lorsque vous appelez une méthode virtuelle via un pointeur de parent ou de référence, le moteur d'exécution appellera le plus dérivé override dans la hiérarchie de l'appel à l'objet.

Lorsqu'une méthode n'est pas virtuelle, et la même méthode est définie plus loin dans la hiérarchie, vous êtes cacher la méthode mère. La différence ici est que lorsque la méthode est appelée par un pointeur de base ou de référence, il appellera la mise en œuvre de base, alors que si elle est appelée dans un objet dérivé, il appellera la mise en œuvre dérivée. Ceci, entre autres, est appelé cacher parce que la base et les fonctions dérivées ne sont pas liés, et l'avoir défini dans la classe dérivée cacher la version de base d'un appel:

struct base {
   virtual void override() { std::cout << "base::override" << std::endl; }
   void not_override() { std::cout << "base::not_override" << std::endl; }
};
struct derived : base {
   void override() { std::cout << "derived::override" << std::endl; }
   void not_override() { std::cout << "derived::not_override" << std::endl; }
};
int main() {
   derived d;
   base & b = d;

   b.override();     // derived::override
   b.not_override(); // base::not_override
   d.not_override(); // derived::not_override
}

La différence, et ce qui est mal dans la réponse par @ erik2red est que overrides sont intimement liés à des fonctions virtuelles et implique qu'il existe un mécanisme de répartition d'exécution en place qui détermine le plus dérivée override pour appeler. Le comportement qui est montré dans la réponse et associée à override est en fait le comportement quand il n'y a pas de dérogation, mais cache plutôt la méthode.

Autres questions

La langue permet virtuelles pures méthodes avec la mise en œuvre. Il ne dit rien sur ce que la terminologie à utiliser avec eux, mais une méthode virtuelle pure ne sera jamais considéré pour l'expédition d'exécution. La raison en est que lorsqu'un cours avec des méthodes virtuelles pures (même si elles sont appliquées) sont considérés comme des classes abstraites, et vous ne pouvez pas instancier un objet de la classe. Une fois que vous avez une classe dérivée qui fournit une mise en œuvre de cette méthode, que la mise en œuvre devient le override finale dans la hiérarchie. La classe peut maintenant être instancié, mais la méthode virtuelle pure ne sera pas appelé par le mécanisme de répartition d'exécution.

Les méthodes virtuelles qui ne sont pas override finale , ainsi que des méthodes hided peuvent être appelées si vous utilisez le nom complet. Dans le cas des méthodes virtuelles, en utilisant le nom complet désactive le mécanisme de répartition polymorphes pour l'appel: d.base::override() appellera la mise en œuvre de base, même s'il y a d'autres overrides pour dériver les classes

.

Une méthode peut cacher d'autres méthodes dans les classes de base, même si les signatures ne correspondent pas.

struct base {
   void f() {}
};
struct derived : base {
   void f(int) {}
};
int main() {
   derived d;
   // d.f() // error, derived::f requires an argument, base::f is hidden in this context
}

Comme overrides , d.base::f() appellera la version de base, non pas parce qu'elle désactive le polymorphisme --il ne pas, comme la méthode n'est pas déclarée virtuelle il jamais behavior-- polymorphes mais parce que la qualification complète indique au compilateur où la méthode est, même si elle était cachée par une autre méthode dans la classe dérivée.

Les fonctions virtuelles existent pour aider à concevoir le comportement d'une classe de base. Une classe de base de fonctions virtuelles pures ne peut pas être instancié et est appelé une classe abstraite.

Il appartient aux classes dérivées pour mettre en œuvre ces méthodes décrites par les fonctions virtuelles dans la classe de base. Les classes dérivées peuvent être instanciés (ils existent et occupent la mémoire).

dérivant de classes dérivées peut redfine une fonction déjà définie dans l'objet parent. Cette technique vous savez déjà comme primant et vous permet de personnaliser le comportement de cet objet enfant.

Comme vous en savoir plus C ++, vous constaterez que l'héritage n'est pas tout ce qu'il est fissuré à l'être. Composition et est souvent une meilleure alternative. Amusez-vous bien.

En venant de Java, on peut trouver le concept de fonctions membres virtuelles par rapport aux non virtuels confus. La chose à retenir est que les méthodes Java correspondent aux fonctions de membres virtuels en C ++.

La question est tellement pourquoi nous avons effectivement des fonctions virtuelles, mais pourquoi nous avons les non-virtuels? La façon dont je me les justifier (me corriger si je me trompe) est qu'ils sont moins chers à mettre en œuvre, les appels à leur peuvent être résolus au moment de la compilation.

L'exemple classique est celui d'un programme de peinture où une classe de forme de base est créé avec une fonction virtuelle tirage (). Ensuite, chacune des formes (cercle, rectangle, triangle, etc.) peuvent être créés sous-classe que chaque outil leur fonction draw () de la manière appropriée et le programme de peinture de base peut tenir une liste des formes qui peuvent chacun faire le tirage approprié ( ) fonction même si seulement un pointeur vers la classe de forme de base est stockée.

La différence est simplement utilisée lorsque vous appelez la méthode de la classe dérivée, par un pointeur vers un objet de classe de base. En ce moment vous si la méthode que vous appelez était prioritaire dans la classe dérivée, vous obtiendrez le exceution de la classe de base, au lieu si était virtuel alors vous avez l'exécution de la méthode de la classe dérivée.

#include <iostream>

class A{
    public:
    virtual void getA() { std::cout << "A in base" << std::endl;};
};

class B : public A {
    public:
    void getA() { std::cout << "A in derived class" << std::endl;}
};

int main(int argc, char** argv)
{
    A a;
    B b;
    a.getA();
    b.getA();

    A* t = new B;
    t->getA();
}

Par exemple:. Dans ce programme t->getA() impression "A in derived class", mais si there'were pas dans la virtuelle modificateur classe de base A, il serait alors imprimer "A in base"

it helps.

Les hélicoptères et les avions volent à la fois, mais ils le font de différentes manières - ils sont les deux cas d'un objet hypothétique Flyer. Vous pouvez demander à l'objet Flyer « voler » -. Mais Flyer est juste une interface Il ne sait pas quoi que ce soit au sujet du vol autre que celui qu'il devrait être en mesure de voler

Cependant, si les deux l'hélicoptère et avion suivre l'interface de dépliant, que si avait un objet d'aérodrome et vous avez donné une brochure tout le terrain d'aviation doit faire est de demander les dépliants à voler.

Par exemple:

Airplace X=Airplane X("boeing 747");
Airfield::takeoff(&X);

Helicopter Y= Helicopter("Comache");
Airfield::takeof(&Y);

void Airfield::takeOff(Flyer * f)
{
     f->fly();
}

C ++ est un langage de type sécurité stricte, et ce genre de fonctionnalité (fonction d'appels de classes dérivées indirectement par une classe de base) est seulement possible lorsque RTTI est activé pour la hiérarchie d'objets, et de qualifier une fonction de membre virtuel permet à cette .

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