Domanda

Sono nuovo in C++.

Qualcuno potrebbe dirmi la differenza tra l'override del metodo e i concetti di funzione virtuale in C++.

La funzionalità delle funzioni virtuali può essere sovrascritta nelle classi derivate.La ridefinizione di una funzione in una classe derivata è detta overriding della funzione.

perché abbiamo effettivamente funzioni virtuali?

È stato utile?

Soluzione

SOMMARIO

In questo lavoro, si discute funzioni virtuali in C ++. Zero pezzo spiega come funzioni virtuali sono dichiarate e sovrascritto. Parte uno tentativi (e forse non riesce) per spiegare come funzioni virtuali sono implementati. La seconda parte è un programma di esempio che utilizza le classi di esempio definite nelle parti zero e uno. La terza parte è l'esempio classico animale dato in ogni funzione virtuale -. Polimorfismo esercitazione

PARTE ZERO

Un metodo di una classe si dice che sia virtual se e solo se il suo dichiarato di essere così.

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

(Naturalmente, io parto dal presupposto che il programmatore non ha in precedenza fare qualcosa di simile #define virtual.)

Una classe che redeclares e ripristina implementa un metodo non virtuale di una delle sue basi è detto sovraccarico tale metodo. Una classe che redeclares e ri-implementa un metodo virtuale di una delle sue basi è detto override tale metodo.

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

PRIMA PARTE

Quando il compilatore rileva una classe ha metodi virtuali, esso aggiunge automaticamente un tabella virtuale metodo (noto anche come vtable ) per la classe di layout memoria. Il risultato è simile a quello che sarebbe stato generato dalla compilazione di questo codice:

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(); }
};

Quando il compilatore rileva una classe ha ignorato un metodo virtuale, sostituisce la sua voce associata nel vtable. Il risultato è simile a quello che sarebbe stato generato dalla compilazione di questo codice:

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; }
};

SECONDA PARTE

Ora che è chiaro che le funzioni virtuali sono realizzati tramite VTables, che non sono altro che un mucchio di puntatori a funzione, dovrebbe essere chiaro che cosa fa questo codice:

#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;
}

PARTE TERZA

Dal momento che nessun esempio di funzione virtuale è completo senza un esempio con gli animali ...

#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();
}

Altri suggerimenti

funzione virtuale / metodo è semplicemente una funzione il cui comportamento può essere sovrascritto in una sottoclasse (o in C ++ termini una classe derivata) ridefinendo come funziona la funzione (utilizzando la stessa firma).

Pensate a un mammifero classe base con una funzione di parlare. La funzione è vuoto e semplicemente Couts come un mammifero parla. Quando si eredita da questa classe è possibile sovrascrivere il metodo parlare in modo che i cani vanno "Arf Arf!" e gatti vanno "Meow Meow".

La tua domanda sembra chiedere quali sono le differenze, così non ce ne sono perché con funzioni virtuali si può ignorare il comportamento di queste funzioni. Si può essere dopo la differenza tra l'override funzioni e sovraccarico.

funzioni sovraccarico mezzi per creare una funzione con lo stesso nome ma diversi argomenti cioè al numero e al tipo di argomento (s) differente. Ecco una spiegazione su sovraccarico in C ++ da di IBM sito :

  

Il sovraccarico (C ++) Se si specifica più di una definizione per un   nome di una funzione o di un operatore nello stesso ambito, si è sovraccaricato   che nome di funzione o operatore. funzioni e operatori overload sono   descritto nelle funzioni sovraccarico (C ++ solo) e sovraccarico   operatori (C ++ solo), rispettivamente.

     

Una dichiarazione sovraccarico è una dichiarazione che era stata dichiarata con   lo stesso nome di una dichiarazione precedentemente dichiarato nello stesso ambito,   tranne che entrambi le dichiarazioni hanno tipi differenti.

     

Se si chiama un nome di funzione sovraccarico o dell'operatore, il compilatore   determina la definizione più appropriato da utilizzare confrontando la   tipi di argomenti che hai utilizzato per chiamare la funzione o l'operatore con il   tipi di parametri specificati nelle definizioni. Il processo di selezione   la funzione sovraccaricata più appropriata o operatore è chiamato   la risoluzione di sovraccarico, come descritto nella risoluzione di sovraccarico (C ++ solo).

Per quanto riguarda il motivo razionale completo per le situazioni in cui sono richieste funzioni virtuali, questo post del blog dà una buona: http://nrecursions.blogspot.in/2015/06/so-why-do-we-need-virtual-functions.html

La differenza tra funzione prioritaria e virtual la funzione diventa importante con polimorfismo.In particolare quando si utilizzano riferimenti o puntatori a una classe base.

La configurazione di base

In C++, qualsiasi classe derivata può essere passata a una funzione che richiede un oggetto di classe base.(Guarda anche Affettare E LSP).Dato:

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);

Nel codice sopra ci sono due classi base, una dichiara un metodo virtuale, l'altra dichiara una funzione non virtuale.

Vengono dichiarate due funzioni che richiedono puntatori alle rispettive classi base.

Le classi derivate

Testiamo ora in particolare il polimorfismo virtual contronon virtuale (metodi prioritari).Le strutture:

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

struct Derived_From_Nonvirtual :Base_Nonvirtual pubblico { void some_function();}

Secondo il linguaggio C++, posso passare un puntatore ad a Derived_From_Virtual A Function_A Perché Derived_From_Virtual eredita da Base_Virtual.Posso anche passare un puntatore a Derived_From_Nonvirtual A Function_B.

La differenza tra virtual e prevalente

IL virtual modificatore dentro Base_Virtual, lo dice al compilatore Function_A userà Derived_From_Virtual::some_virtual_function() invece del metodo in Base_Virtual.Questo perché il metodo lo è virtuale, la definizione finale può risiedere in a futuro O derivato classe.La definizione effettiva dice di utilizzare il metodo nella classe più derivata contenente la definizione.

Quando si passa un puntatore a Derived_From_Nonvirtual A Function_B, il compilatore ordinerà alla funzione di utilizzare il metodo della classe base, Base_Nonvirtual::some_function().IL some_function() Il metodo nella classe derivata è un metodo separato, non correlato, dalla classe base.

La differenza principale tra virtual e l'override avviene con il polimorfismo.

Scopri C ++ FAQ lite, http://www.parashift.com/c++-faq -lite / . è probabilmente uno dei migliori C ++ risorse per i principianti. ha approfondito write-up sulle funzioni virtuali e prioritario.

Personalmente ho trovato C ++ FAQ di essere una fonte eccellente come ho imparato C ++. Altre persone hanno opinioni diverse, la vostra situazione potrebbe essere diversa

Questo è più di un follow-up sui commenti da questo rispondere di una risposta da solo.

virtual è una parola chiave che le richieste di esecuzione la spedizione per il metodo viene dichiarato e, allo stesso tempo dichiara il metodo come una delle override (implementato metodi virtuali puri a parte). Il metodo di essere dichiarata, e qualsiasi metodo che condivide la firma e il nome esatto nella gerarchia derivanti da questa classe verso il basso sono override . Quando si chiama un metodo virtuale tramite un puntatore genitore o di riferimento, il runtime chiamerà il più derivata di override nella gerarchia della chiamata oggetto.

Quando un metodo non è virtuale, e lo stesso metodo viene definito più avanti nella gerarchia, si è nasconde il metodo genitore. La differenza qui è che quando il metodo viene chiamato tramite un puntatore base o di riferimento si chiamerà l'implementazione di base, mentre se viene chiamato in un oggetto derivato chiamerà l'implementazione derivata. Questo, tra gli altri casi, si chiama nascosto perché la base e funzioni derivate non sono correlati, e averlo definito nella classe derivata sarà nascondere la versione base da una chiamata:

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 differenza, e ciò che è sbagliato nella risposta da @ erik2red è che override sono intimamente legati alla funzioni virtuali e implica che non v'è un meccanismo di esecuzione alla spedizione, in luogo che determina il più derivato di override per chiamare. Il comportamento che viene mostrato nella risposta ed associato a override è in realtà il comportamento quando non ci sono sostituzioni ma piuttosto metodo nascondere.

Altri temi

Il linguaggio permette di metodi con implementazione puri virtuali. Non dice nulla di ciò che la terminologia deve essere usato con loro, ma un metodo virtuale puro non potrà mai essere considerato per la spedizione runtime. La ragione è che quando un classi con metodi virtuali puri (anche se implementato) sono considerati classi astratte, e non è possibile creare un'istanza di un oggetto della classe. Una volta che hai una classe derivata che fornisce un'implementazione per quel metodo, che l'attuazione diventa il di override finale nella gerarchia. La classe può ora essere istanziato, ma il metodo virtuale puro non sarà chiamato attraverso il meccanismo di esecuzione spedizione.

metodi virtuali che non sono il di override finale , così come i metodi hided può essere chiamato se si utilizza il nome completo. Nel caso di metodi virtuali, utilizzando il nome completo disabilita il meccanismo spedizione polimorfico per la chiamata: d.base::override() chiamerà l'implementazione di base, anche se ci sono altri override nel derivare classi

.

Un metodo può nascondere altri metodi in classi base, anche se le firme non corrispondono.

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
}

Come per override , d.base::f() chiamerà la versione base, non perché disabilita il polimorfismo --è non lo fa, come il metodo non è dichiarato virtuale non avrà mai behavior-- polimorfico, ma perché la qualifica piena dice al compilatore dove il metodo è, anche se è stato nascosto da un altro metodo nella classe derivata.

Le funzioni virtuali esistono per contribuire a progettare il comportamento di una classe base. Una classe di base di funzioni virtuali pure non può essere istanziata ed è chiamata una classe astratta.

E 'fino alle classi derivate di implementare i metodi descritti dalle funzioni virtuali nella classe base. Le classi derivate possono essere istanziati (esistono e occupano memoria).

Dato dalla classi derivate può redfine una funzione già definito nell'oggetto genitore. Questa tecnica si conosce già come l'override e consente di personalizzare il comportamento di questo oggetto secondario.

Come si impara di più C ++, troverete che l'ereditarietà non è tutto ciò che è incrinato fino a essere. Composizione ed è spesso un'alternativa migliore. Buon divertimento.

Quando provenienti da Java, si potrebbe trovare il concetto di virtual vs funzioni membro non virtuali confuse. La cosa da ricordare è che i metodi Java corrispondono alle funzioni membro virtuali in C ++.

Il problema non è tanto il motivo per cui abbiamo effettivamente funzioni virtuali, ma perché dobbiamo quelli non virtuali? Il modo in cui li giustifico a me stesso (correggetemi se sbaglio) è che sono meno costosi da implementare, come le chiamate a loro possono essere risolti in fase di compilazione.

L'esempio classico è quello di un programma di disegno in cui si crea una classe Shape base con una funzione draw () virtuale. Poi ciascuna delle forme (cerchio, rettangolo, triangolo, ecc) può essere creato come sottoclasse che ogni attrezzo loro funzione draw () in modo appropriato e il programma di disegno nucleo può mantenere un elenco di forme che vi ciascun fare sorteggio appropriata ( ) funzione anche se solo un puntatore alla classe Shape base viene memorizzato.

La differenza è solo utilizzato quando si chiama il metodo della classe derivata, tramite un puntatore ad un oggetto di classe di base. In quel momento se il metodo si sta chiamando è stato di override nella classe derivata, si otterrà l'exceution della classe base, invece, se fosse virtuale, allora si ha l'esecuzione del metodo della classe derivata.

#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();
}

Ad esempio:. In questo programma t->getA() "A in derived class" di stampa, ma se non there'were Modificatore virtuale nella classe base A, allora sarebbe stampare "A in base"

Speranza che aiuta.

Elicotteri e aeroplani volano entrambi, ma lo fanno in modi diversi - sono entrambe le istanze di alcuni Flyer oggetto ipotetica. Si può chiedere l'oggetto Flyer a "volare" -. Ma Flyer è solo un'interfaccia Non sa nulla di volando diverso da quello che dovrebbe essere in grado di volare

Tuttavia, se sia l'elicottero e aereo seguono l'interfaccia del volantino, che se aveva un oggetto campo d'aviazione e ti ha dato un volantino tutto il campo di aviazione ha bisogno di fare è di chiedere i volantini a volare.

Ad esempio:

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

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

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

C ++ è un tipo-safe rigoroso linguaggio, e questo tipo di funzionalità (fare chiamate di funzione derivata classi indirettamente attraverso una classe base) è possibile solo quando RTTI è attivata per la gerarchia di oggetti, e qualificare una funzione membro virtuale permette questo .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top