2014-07-19 27 views
5

Sto tentando di aggiungere un numero di elementi derivati ​​da sottoclassi in un vettore, iterare attraverso di essi chiamando un metodo sottoposto a override e voglio che invochi il metodo sottoposto a override ove possibile. Tuttavia, ho scoperto che sembra chiamare solo il metodo della superclasse.10 Chiamare i metodi di sottoclasse dalla superclasse in un vettore C++

Ho imparato Java e non so perché lo stia facendo in C++. Ho provato a riscrivere il codice usando un vettore di puntatori della superclasse e gettando il puntatore della sottoclasse sulla superclasse. Accedere a questo tramite i puntatori quindi funziona.

Idealmente non voglio dover inserire un elenco di puntatori nel vettore da allora devo eliminare manualmente ciascuno (credo?) Per fermare perdite di memoria poiché creerò gli oggetti con nuovi in ​​modo che persistano oltre la chiamata al metodo per aggiungerli al vettore.

C'è un modo migliore per farlo o sono bloccato a utilizzare i puntatori e chiamare eliminare sugli oggetti creati quando la classe genitore non è necessaria? Preferibilmente il vettore sarebbe un elenco di classe X, piuttosto che una lista di puntatori di classe X

La mia struttura è:

class a { vector vec of class X, 
    method to create and add an instance of X into vector vec, 
    method to create and add an instance of Y into vector vec } 
class X { talk() } 
class Y : public X { talk() } 

codice per dimostrare quello che idealmente voglio fare, ma mostrando la sua rotto da solo chiamando il metodo della superclasse:

#include <stdio.h> 
#include <stdlib.h> 
#include <iostream> 
#include <vector> 

class A { 
    public: 
    virtual void talk() { printf("A\n"); } 
}; 

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

int main(void) { 
    std::vector<A> vec; 
    std::vector<A*> vec2; 
    A a; 
    B b; 
    a.talk(); 
    b.talk(); 

    vec.push_back(a); 
    vec.push_back(b); 
    vec2.push_back(&a); 
    vec2.push_back(&b); 

    for(int i = 0; i < vec.size(); i++) { 
     vec[i].talk(); 
     vec2[i]->talk(); //bad but short for example 
    } 

} 
+2

Sono necessari puntatori (possibilmente puntatori intelligenti, come 'std :: unique_ptr') per ottenere un comportamento polimorfico. Vedi ["slicing degli oggetti"] (http://en.wikipedia.org/wiki/Object_slicing). Funziona in Java perché lì, 'MyClass obj' dichiara effettivamente un puntatore a' MyClass'. –

+1

'talk()' non è virtuale in 'A', quindi non esiste alcun metodo per sovrascrivere. Il compilatore andrà con quello che è il tipo statico, che è 'A'. – 0x499602D2

+0

@ 0x499602D2 Errore nella prima versione di questo codice, risolto, aggiunto virtuale, ancora non funzionante come previsto. – Chewett

risposta

3

Per ottenere il comportamento polimorfico desiderato, è necessario aggiungere l'identificatore virtual alle funzioni nella classe base che si desidera sovrascrivere nelle classi derivate.

class A { 
public: 
    virtual void talk() { printf("A\n"); } 
}; 

Si dovrebbe anche fare l'abitudine di aggiungere il override identificatore sulle funzioni override nelle classi derivate in modo che il compilatore può aiutare con questo tipo di problemi.

class B: public A { 
public: 
    virtual void talk() override { printf("B\n"); } 
//     ^Compiler will report an error if base class' function 
//      is not virtual. 
}; 

Inoltre non è possibile assegnare un oggetto derivato a un'istanza di una classe base, o slicing will occur.

std::vector<A> vec; 
/* ... */ 
B b; 
/* ... */ 
vec.push_back(b); // Slicing. Information only in B is lost. 

Live example using virtual specifier

Live example without virtual specifier

+0

Che non funziona FYI visto che sto ancora ottenendo AB AA AB dall'output. – Chewett

+0

Originariamente il codice era stato digitato in modo errato, fissato sul mio IDE ma non qui, corretto ora. – Chewett

+0

Potrebbe voler rimuovere la prima parte della risposta in quanto non era un problema (la semplice copia del codice era lol). Altrimenti sembra fantastico, grazie :) – Chewett

0

Si dovrebbe dichiarare i metodi come virtual al fine di essere in grado di ignorare loro in una sottoclasse. Inoltre è buona pratica rendere virtuale il distruttore.

class A { 
public: 
    virtual void talk() { printf("A\n"); } 
    virtual ~A(){} 
}; 

class B: public A { 
public: 
    // using virtual is not really necessary here, but it's good for clarity. 
    virtual void talk() { printf("B\n"); } 
}; 
+0

Si è verificato un errore durante il caricamento del codice, l'ho risolto prima del caricamento e ho ancora l'output AB AA AB – Chewett

0

Il metodo deve essere virtual.
Nei metodi java sono virtuali per impostazione predefinita.

class A { 
    public: 
    virtual void talk() { printf("A\n"); } 
}; 

class B: public A { 
    public: 
     virtual void talk() override { printf("B\n"); } //override key word is in C++ 0x and above 
}; 
+0

Si è verificato un errore nel caricamento del codice, l'ho corretto prima di caricare e ho ancora l'output AB AA AB – Chewett

0

Penso che quello che vi manca è la parola chiave virtual nella vostra dichiarazione di metodo. Se si desidera ottenere un metodo di sottoclasse durante il richiamo di metodi in una classe genitore, è necessario dichiarare i metodi virtual.

+0

Quello era un errore nel caricare il codice, l'ho risolto prima di caricare e ho ancora l'output AB AA AB – Chewett

0

Se non si utilizza un puntatore si otterrà 'oggetto slicing' quando si copia l'oggetto nel vettore. Questo riduce l'oggetto al tipo di base dichiarato nell'argomento del modello vettoriale. Quindi non esiste una sottoclasse, quindi nessun metodo di sottoclasse da chiamare, anche se il metodo è virtuale.

+0

@downvoter Oh davvero? – EJP