2011-02-05 11 views

risposta

22

C'è un problema qui: questa sarebbe una violazione diretta del principio di sostituzione di liskov, vale a dire A non sarebbe agire come unB più.

Se si desidera riutilizzare B implementazione, la soluzione è semplicemente quello di fare in modo:

class A 
{ 
public: 
    void foo() { return b.foo(); } 
    void bar() { return b.bar(); } 
    // ... 

private: 
    B b; 
}; 

Non abusare di successione, l'uso composizione invece

+4

Capita di dover scrivere una classe che era il 99% di un'altra classe, ma gestire le cose in modo diverso e aveva 1 funzione, solo una funzione che non poteva esistere B. – rxantos

1

Se i metodi sono privati ​​in B, allora rimarranno nascoste ad una, anche se si utilizza l'ereditarietà pubblica.

5

Non è possibile "nasconderlo" di per sé, ma è possibile effettuare un errore in fase di compilazione per chiamarlo. Esempio:

struct A 
{ 
    void AMethod() {} 
}; 

class B : public A 
{ 
    void AMethod() {} //Hides A::AMethod 
}; 

int main() 
{ 
    B myB; 
    myB.AMethod(); //Error: AMethod is private 
    static_cast<A*>(&myB)->AMethod(); //Ok 
    return 0; 
} 

Esempi sulla tastiera codici with the error e without.

Ciò detto, nonostante sia possibile, non dovresti farlo. Ti confonderai con i clienti.

MODIFICA: si noti che è anche possibile farlo con virtual functions (e with the error).

+0

Non penso che sia necessario definire l'AMetodo (privato) nell'erogazione della classe B. La dichiarazione, credo, basti. Direi che il metodo originale è "nascosto", se si tratta di un errore in fase di compilazione cercando di usarlo (anche se il messaggio di errore differisce dal caso in cui il metodo non esiste affatto). –

+0

@eq: è corretto, non è necessario definirlo a meno che il metodo nella classe genitore sia 'virtuale'. (Sono appena diventato pigro e ho usato copia/incolla per rendere 'B') –

0

Impossibile modificare la visibilità del metodo originale.

si potrebbe creare un metodo in struct A con lo stesso nome e che hanno il metodo sia privato, ma che non impedisce il metodo venga chiamato quando un'istanza di struct A viene fatto riferimento da una variabile di tipo B.

0

Perché non lo si rende virtuale nella classe base e si sovrascrive nei suoi figli? (more help)

34

Se si desidera nascondere selettivamente le funzioni da B, non ha molto senso utilizzare l'ereditarietà pubblica in primo luogo.
utilizzare l'ereditarietà privata & selettivamente portare metodi da B nel campo di applicazione A:

struct B{ 
    void method1(){}; 
    void method2(){}; 
}; 
struct A : private B{ 
    using B::method1; 
}; 

A a; 
a.method1(); 
a.method2(); //error method2 is not accesible 
3

a quelli che sono suggerendo composizione .. questo potrebbe non essere il miglior modo possibile di fare le cose. La mia comprensione è che il Principio di sostituzione di Liskov afferma solo che esiste la possibilità che le funzioni della classe base vengano usate sul bambino, non che debbano necessariamente esserlo. Ad esempio, per una particolare classe base si possono avere più funzioni che essenzialmente eseguono la stessa operazione, ma per casi specifici diversi. Nella classe derivata potresti voler astrarre queste funzioni pubbliche in favore della semplificazione dell'interfaccia dell'utente. È qui che si può usare l'ereditarietà privata. Anche l'ereditarietà privata può essere una necessità, se abbiamo funzioni protette nella classe base che non vogliamo che l'utente della classe base chiami, ma sarebbe inestimabile per la classe derivata.

In breve, se si deve, utilizzare l'ereditarietà privata, ma la composizione è preferita nella maggior parte dei casi.

8

A parte le modalità descritte nel precedente risposte-composizione, l'ereditarietà privata, e l'ereditarietà non privato ma con il metodo ereditato dichiarato privato, un altro modo è quello esplicitamente delete il metodo ereditato:

#include <iostream> 

struct A { 
    void foo() { std::cout << "foo\n"; } 
}; 

struct B : A { 
    void foo() = delete; 
}; 

int main() { 
    B b; 
    b.foo(); // COMPILER ERROR 
} 

Anche se la chiamata b.foo() produce un errore di compilazione, il codice client può ancora chiamare la versione della classe base, qualificando con l'identificatore di classe di base A:

b.A::foo(); // compiles, outputs 'foo' to console 

Questo expli modo di cancellazione cit funziona quando foo è non un metodo virtuale non cancellato in A. Con C++ 11 Standard §10.3/16, questa cancellazione esplicita è mal formata quando il metodo eliminato nella classe derivata sovrascrive un metodo virtuale non cancellato della classe base. Per maggiori informazioni su questa restrizione, vedere le risposte alla domanda SO C++11 Delete Overriden Method.

1

C'è ancora un altro approccio.

class A{ 
    void f1(); 
    void f2(); 
    void f3(); 
} 

class BInterface{ 
    void f2(); 
    void f3(); 
} 

class B : public A, BInterface 
{ 
} 

BInterface b = new B(); 
b->f1(); //doesn't work since f1 is not declared in BInterface 
b->f2(); //should work 
b->f3(); //should work 
delete(b); 

Utilizzare BInterface come filtro per le classi ereditate per escludere metodi indesiderati. In questo caso, il principio di sostituzione di Liskov non viene violato poiché un oggetto della classe BInterface non è un oggetto di classe A anche se un oggetto di classe B è un oggetto della classe BInterface.