Best practice per funzioni virtuali ereditate non utilizzate (implementazione dei metodi della superclasse)

1

Supponiamo di avere una gerarchia di più classi ciascuna derivata l'una dall'altra utilizzando funzioni virtuali. La classe base funge da interfaccia e definisce funzioni, che potrebbero non essere richieste da tutte le classi derivate. Ora mi chiedo quale sia la migliore pratica per tali funzioni intermedie. Dovrebbero essere ancora implementati e collegati in modo semplice alla classe base, o dovrebbero essere saltati? Il problema che ho è che l'implementazione di tale funzione, quando non è necessaria, aggiunge semplicemente rumore al codice. D'altra parte, quando si usa una classe superiore, in C ++ non si può semplicemente chiamare un metodo superclasse (come in Java), quindi è necessario sapere in quale classe base il metodo è realmente implementato. E a mio parere, questo violerebbe l'incapsulamento, perché avrei bisogno di conoscere alcuni dettagli di implementazione della classe base.

Per illustrare cosa intendo, ecco un exmaple artificiale:

class MyInterface
{
    virtual void foo(bool) = 0;
    virtual void foo1(int) = 0;
};

class Base : public MyInterface
{
    void foo(bool param) override
    {
        // do some stuff here.
    }

    void foo1(int param) override
    {
        // do some stuff here.
    }
};


class A : Base
{
    void foo(bool param) override
    {
        // do some stuff here.
        Base::foo(param);
    }
    // class doesn't need foo1() here so it is not implemented.
};


class B : A
{
    void foo(bool param) override
    {
        // do some stuff here.
        A::foo(param);
    }

    void foo1(int param) override
    {
        // This class needs foo1() but as A doesn't have it implemented
        // I have to knwo this and skip A going directly to Base
        Base::foo1(1);
    }
};

Qui la classe B deve sapere che A non implementa foo1 e deve saltarlo. Naturalmente questo può essere visto nell'intestazione, ma se voglio cambiare in seguito la classe A non dovrebbe essere necessario toccare tutte le altre classi che ne derivano (principio di incapsulamento e isolamento) Quindi, ancora peggio, se io implementa ora foo1() in A più tardi, B lo salterà comunque e non realizzerà mai che la funzionalità è cambiata.

Quindi dal punto di vista del modello di un oggetto presumo che tali funzioni vuote debbano ancora essere progettate in una classe?

    
posta Devolus 29.08.2014 - 09:29
fonte

2 risposte

1

Hai ragione, questo è un difetto del linguaggio stesso, il che significa che nonostante tutti i vantaggi derivanti dall'ereditarietà, ancora devi sapere come è implementato. Sarebbe ideale poter chiamare super all'interno del metodo e farlo chiamare il metodo appropriato, qualunque esso sia. Questa è una delle cose su cui Java ha cercato di migliorare.

Come hai detto, puoi aggirarlo implementando tutti i metodi in classe A, e così facendo, previeni le complicazioni in seguito. È un po 'un dolore, ma suppongo sia un male necessario per creare una libreria appropriata da usare per gli altri. Nota che solo la classe resa disponibile nella libreria per l'uso deve implementare tutti i metodi virtuali e non tutte le classi utilizzate internamente a meno che tu non richieda diversamente.

Sebbene non sia una soluzione, un approccio leggermente più amichevole (anche se un po 'hacky) potrebbe essere quello di digitare "super" come definizione di tipo privato per la classe base. Come nel caso della classe B, avresti:

private:  
    typedef A super;

Allo stesso modo in classe A, puoi definire:

private:
    typedef Base super;

Almeno se sei tu a scrivere il codice, piuttosto che conoscere il nome della classe, potresti fare:

class B : A
{
    void foo(bool param) override
    {
        // do some stuff here.
        super::foo(param);
    }

    void foo1(int param) override
    {
        super::super::foo1(1);
    }
};

Ancora una volta, anche se questo non risolve il tuo problema, lo rende leggermente più gestibile.

Vedi qui per ulteriori dettagli.

    
risposta data 29.08.2014 - 11:17
fonte
1

Basta chiamare la versione della classe base immediata della funzione. Se quella funzione non è implementata in quella classe base immediata, il compilatore chiama quello nel genitore di quella base e così via fino a quando non trova un antenato che implementa effettivamente la funzione.

    
risposta data 02.01.2018 - 08:55
fonte

Leggi altre domande sui tag