2009-07-01 17 views
17

Sto imparando a conoscere il C++ in una classe in questo momento e non ho abbastanza funzioni virtuali pure. Capisco che siano successivamente delineati in una classe derivata, ma perché dovresti dichiararlo uguale a 0 se lo definisci solo nella classe derivata?Quali sono gli usi delle funzioni virtuali pure in C++?

+2

Per te e per i pochi che ti hanno votato: il manuale non spiegava il perché; e siccome sono sicuro che qualcun altro lo farà su Google, l'ho chiesto qui. In questo modo qualcun altro può trarne beneficio. – patricksweeney

+0

Che libro di testo era quello, allora? Francamente, le risposte qui (come è tipico per tali domande generali) non sono né particolarmente buone né accurate. Quindi non senti che stai facendo un servizio ponendo domande come questa. –

+0

potresti chiederti perché vuoi che una classe base sia astratta. è, tra le altre cose, controllare l'utilizzo della gerarchia di classi, ricordare quando si creano classi non solo modellando il problema, ma occorre anche tener conto di come verranno utilizzate in futuro e da altri. –

risposta

30

In breve, è rendere astratta la classe, in modo che non possa essere istanziata, ma una classe figlio può ignorare i metodi virtuali puri per formare una classe concreta. Questo è un buon modo per definire un'interfaccia in C++.

+1

Consiglierei di cambiare: "... una classe bambino può ..." a "... una classe foglia deve ...". –

+0

Ho cercato di rimanere intenzionalmente vago per consentire la possibilità di un bambino che sovrascriva alcuni ma non tutti i metodi virtuali puri, il che si traduce in una classe base astratta che richiede un minor numero di sostituzioni per diventare concreti. Tuttavia, non lo definirei una foglia perché anche un bambino concreto può avere figli propri e alcuni di essi possono essere astratti. In effetti, una classe può sovrascrivere simultaneamente tutti i metodi virtuali in precedenza pur dichiarandone di nuovi. Detto questo, penso che sia probabilmente meglio lasciare la spiegazione originale, breve, con questo commento prolisso. –

+1

Il problema è probabilmente solo uno dei termini. IMHO, una funzione è puramente virtuale se non è stata implementata in una delle classi derivate. In altre parole, se una classe a qualsiasi livello ha ancora una pura funzione virtuale, allora quella classe è ancora astratta. Da qui la mia richiesta. Ma direi che questi commenti probabilmente spiegano abbastanza le cose a questo punto! :) –

12

Ciò impone a una classe derivata di definire la funzione.

0

L'idea con classi astratte è che si può ancora avere una variabile dichiarata con quel tipo (vale a dire, è il tipo statico), ma la variabile effettivamente fa riferimento o punta a un concreto tipo concreto (il tipo dinamico).

Quando si richiama un metodo in C++, il compilatore deve assicurarsi che il metodo sia supportato su quell'oggetto.

Dichiarando la pura funzione virtuale, si sta mettendo un "segnaposto" che il compilatore può usare per dire "oh ... so che qualsiasi cosa a cui si fa riferimento con questa variabile accetterà quella chiamata" perché l'effettiva tipi concreti lo implementeranno. Tuttavia, non è necessario fornire un'implementazione nel tipo astratto.

Se non hai dichiarato nulla, il compilatore non avrebbe alcun modo efficace per garantire che sarebbe implementato da tutti i sottotipi.

Ovviamente, se stai chiedendo perché vorresti fare un abstract di classe, ci sono molte informazioni in merito.

4

I metodi virtuali puri in C++ sono fondamentalmente un modo per definire le interfacce senza richiedere che vengano implementate.

+1

Correggere, più precisamente: "un modo per forzare tutti gli eredi seguire la firma definita" –

3

Per aggiungere alla risposta di Steven sudit:

"In breve, è di rendere la classe astratta, in modo che non possa essere istanziato, ma una classe figlia può ignorare i metodi virtuali puri a formare una classe concreta. Questo è un buon modo per definire un'interfaccia in C++. "

Un esempio di questo sarebbe se si avesse una classe base (forse Shape) che si usa per definire un numero di funzioni membro che possono essere utilizzate dalle sue classi derivate, ma si vuole impedire un'istanza di Shape dichiarata e forzare gli utenti di usare solo le classi derivate (che può essere, rettangolo, triangolo, del Pentagono, e così via)

RE: la risposta di Jeff sopra

classi non astratte possono contenere funzioni membro virtuali e un'istanza. Infatti, per il sovraccarico delle funzioni membro è necessario in quanto di default C++ non determina il tipo di runtime di una variabile, ma quando viene definito usando la parola chiave virtuale, lo farà.

Considerate questo codice (nota, di accesso, mutatori, costruttori, ecc non sono inclusi per motivi di chiarezza):

class Person{ 
    int age; 

    public: 
    virtual void print(){ 
     cout << age <<endl; 
    } 
} 

class Student: public Person{ 
    int studentID 

    public: 
    void print(){ 
     cout << age << studentID <<endl; 
    } 
} 

Ora, durante l'esecuzione di questo codice:

Person p = new Student(); 
p.print(); 

, senza il virtuale parola chiave, verrà stampata solo l'età, non l'età e lo studenteID come dovrebbe avvenire per la classe Student

(questo esempio si basa su uno molto simile a C++ per r java programmers http://www.amazon.com/Java-Programmers-Mark-Allen-Weiss/dp/013919424X)

@Steven Sudit: sei completamente corretto, ho trascurato di includere l'ereditarietà effettiva, doh! Gli accessors, ecc. Non sono inclusi per mantenere le cose più chiare, e l'ho reso più ovvio ora. 3-7-09: tutto risolto

+0

Credo che il tuo esempio sia rotto, in quanto non include l'ereditarietà, e non c'è modo di impostare mai i valori dei membri di dati privati . L'idea alla base è corretta, ovviamente. –

+0

È meglio, ma non è ancora corretto: la classe figlio non dovrebbe avere il proprio membro dei dati sull'età. –

+0

Inoltre, l'istanza in basso manca di parentesi attorno al costruttore. –

7

Qualsiasi classe contenente un metodo virtuale puro sarà astratta, ovvero non può essere istanziata. Le classi astratte sono utili per definire alcuni comportamenti di base che le sottoclassi dovrebbero condividere, ma consentendo (in effetti, richiedendo) sottoclassi di implementare l'abstract singolarmente.

Un esempio di una classe astratta:

class Foo { 

    // pure virtual, must be implemented by subclasses 
    virtual public void myMethod() = 0; 

    // normal method, will be available to all subclasses, 
    // but *can* be overridden 
    virtual public void myOtherMethod(); 
}; 

Una classe in cui ogni metodo è astratto può essere utilizzato come interfaccia, che richiede tutte le sottoclassi di conformarsi all'interfaccia attuando tutti i metodi contenuti in esso.

Un esempio di un'interfaccia:

class Bar { 

    // all method are pure virtual; subclasses must implement 
    // all of them 
    virtual public void myMethod() = 0; 

    virtual public void myOtherMethod() = 0; 
}; 
+0

Penso che intendessi la prima frase come "Qualsiasi classe che contiene un metodo * pure * virtuale" .... – Dan

+0

Probabilmente aggiungerei che una classe base astratta può contenere non solo implementazioni di metodi, ma anche i membri dei dati. Un'interfaccia non dovrebbe avere né In C++, questa è solo una convenzione. In C# o Java, è una regola. –

3

Immagina voglio modellare diversi tipi di forme, e tutte hanno una zona ben definita. Decido che ogni forma deve ereditare IShape ("I" per l'interfaccia), e IShape includerà un metodo GetArea():

class IShape { 
    virtual int GetArea(); 
}; 

Ora il problema: come dovrei calcolare l'area di una forma, se quella forma non lo fa override GetArea()? Cioè, qual è la migliore implementazione di default? I cerchi usano il pi * raggio^2, i quadrati usano la lunghezza^2, i parallelogrammi ei rettangoli usano l'altezza di base *, i triangoli usano 1/2 base * altezza, i rombi, i pentagoni, gli ottagoni, ecc. Usano altre formule.

così dico "se sei una forma è necessario definire un modo per calcolare l'area, ma dannato se so quello che sarà", definendo il metodo puro virtuale:

class IShape { 
    virtual int GetArea() = 0; 
}; 
+0

Non è necessario che GetArea sia dichiarato virtuale in entrambi i casi? –

+0

Deve essere dichiarato virtuale nella classe base. Grazie per la correzione. –

1

In sostanza, i veri virtual sono usati per creare un'interfaccia (simile a java). Questo può essere usato come un accordo tra due moduli (o classi, o qualsiasi altra cosa) su quale tipo di funzionalità aspettarsi, senza dover sapere nulla sull'implementazione dell'altro pezzo. Questo ti permette di collegare e suonare facilmente pezzi usando la stessa interfaccia senza dover cambiare nulla nell'altro modulo che sta usando la tua interfaccia.

Ad esempio:

class IStudent 
{ 
    public: 
    virtual ~IStudent(){}; 
    virtual std::string getName() = 0; 
}; 


class Student : public IStudent 
{ 
    public: 
    std::string name; 
    std::string getName() { return name; }; 
    void setName(std::string in) { name = in; }; 
}; 

class School 
{ 
    public: 
    void sendStudentToDetention(IStudent *in) { 
     cout << "The student sent to detention is: "; 
     cout << in->getName() << endl; 
    }; 
}; 

int main() 
{ 
    Student student; 
    student.setName("Dave"); 

    School school; 
    school.sendStudentToDetention(&student); 
return 0; 
} 

La scuola non ha bisogno di sapere come impostare il nome di uno studente, tutto ciò che serve sapere è come ottenere il nome dello studente.Fornendo un'interfaccia per lo studente da implementare e la scuola da utilizzare, c'è un accordo tra i due pezzi su quale funzionalità è necessaria alla scuola per svolgere il proprio lavoro. Ora possiamo attivare e disattivare diverse implementazioni della classe Student che vogliamo senza influire sulla scuola (a patto che implementiamo la stessa interfaccia ogni volta).

+0

Suggerirei che il membro di dati "nome" nello Studente dovrebbe essere privato o almeno protetto. Raccomando anche di passare lo studente per riferimento, non per puntatore. Infine, una classe di studenti del mondo reale probabilmente prenderebbe il nome nel suo costruttore. –

+0

concordo con tutto ciò. quelli erano solo sviste quando ho digitato questo esempio molto velocemente. – davidivins

Problemi correlati