2011-01-22 10 views
9

Sono novizio in C++ e ho molti anni di esperienza sui linguaggi OO come C/C#/Objective-C. Ora sto imparando C++.Modificatore di accesso alla sottoclasse C++?

ho visto questo codice C++:

class World : public State 
    { 
    }; 

Sembra classe World eredita la classe State pubblicamente. Sottoclasse pubblica? È difficile da capire.

Qual è il concetto di questa funzione? E quando è utile o richiesto?

+0

Intendevi scrivere 'class World: private State {}'? In quello che hai effettivamente scritto, l'eredità è pubblica. –

+0

Intendevi 'class World: private State'? – detunized

+0

@Steve, @detunized Mi dispiace per il mio stupido errore. Ho modificato la domanda. – Eonil

risposta

13

La necessità per la parola chiave public è proprio quella per le classi definite con la parola chiave class, il modificatore di accesso predefinito (per tutto - membri dati, funzioni membro e classi base) è private. Così

class World : State {}; 

è lo stesso:

class World : private State {}; 

e che probabilmente non è ciò che si vuole - significa che la classe base è accessibile solo all'interno della classe World. Gli estranei "non sanno" che l'eredità esiste.

Per classi definite con la parola chiave struct, il modificatore di accesso di default è public, in modo da potrebbe scrittura:

struct World : State {}; 

e ottenere qualcosa che sia sembra e si comporta un po 'come tutte le altre lingue con l'ereditarietà. Ma la parola chiave struct e il fatto che definisce una classe, è in realtà solo lì per compatibilità con C.Non troverai molte guide in stile C++ che consigliano di utilizzarlo solo per ottenere l'accessibilità pubblica predefinita, in genere viene utilizzato solo per le classi che sono POD, o forse solo per le classi senza funzioni membro.

Per quanto riguarda il motivo per cui C++ ha ereditarietà privata in primo luogo: per la maggior parte degli scopi, l'ereditarietà privata è una forma di composizione. Composizione normale:

class World { 
    State state; 
    public: 
    void foo() { 
     state.bar(); 
     state.baz(); 
     and so on 
    } 
}; 

Cioè, il mondo di classe sa che è implementato utilizzando uno Stato, e il mondo esterno non sa come mondiale è implementato.

vs.

class World : private State { 
    public: 
    void foo() { 
     bar(); 
     baz(); 
     and so on 
    } 
}; 

Cioè, il mondo di classe sa che è implementato da essendo uno Stato, e il mondo esterno non sa come è implementato. Ma puoi esporre selettivamente parti dell'interfaccia di stato inserendo ad esempio using State::bar; nella parte pubblica della definizione di World. L'effetto è come se scriveste faticosamente una funzione (o diversi sovraccarichi) in World, ognuno dei quali delega alla stessa funzione su State.

Oltre a evitare la digitazione, un uso comune dell'ereditarietà privata è quando la classe è vuota, ovvero non ha membri di dati. Quindi se si tratta di un membro di World, deve occupare spazio (ovviamente, a seconda del layout dell'oggetto questo potrebbe essere lo spazio che altrimenti sarebbe solo il padding, quindi non necessariamente aumenta la dimensione di World), ma se è una classe base quindi una cosa chiamata "ottimizzazione della classe base vuota" entra in gioco e può essere a dimensione zero. Se stai creando un lotto, questo potrebbe importare. L'ereditarietà privata consente l'ottimizzazione, ma il mondo esterno non inferisce una relazione "è-a", perché non vede l'ereditarietà.

È una bella differenza. In caso di dubbio, usa solo la composizione esplicita. L'introduzione dell'ereditarietà per risparmiare la digitazione è molto buona fino a quando non ha conseguenze inaspettate.

+0

Grazie. Accettato perché l'ultimo paragrafo :) – Eonil

3

Cosa ti fa pensare che sia privato? Dice pubblico proprio lì, il che significa che è pubblicamente sottoclasse.

A parte ciò, l'ereditarietà privata e protetta è uguale all'ereditarietà pubblica, con la differenza che tutte le variabili membro sono funzioni ereditate con almeno accessibilità privata o protetta. Ad esempio, se State avesse una funzione membro pubblico 'foo()', sarebbe privata in 'Mondo'.

Questo è raramente utilizzato nella pratica, ma ha lo scopo. L'uso più comune che ho visto è la composizione attraverso l'ereditarietà. cioè si desidera una relazione "ha una" piuttosto che una "è una" (che di solito si ottiene con l'ereditarietà pubblica). Con l'ereditare privatamente la classe, si ottengono tutte le sue variabili e metodi, ma non li si espone al mondo esterno.

Un vantaggio dell'utilizzo dell'ereditarietà privata per la composizione deriva dall'ottimizzazione della classe base vuota (EBCO). Usando la composizione normale, avere un oggetto membro di una classe vuota userebbe comunque almeno 1 byte perché tutte le variabili devono avere un indirizzo univoco. Se si eredita privatamente l'oggetto di cui si desidera essere composto, ciò non si applica e non si verificherà alcuna perdita di memoria.

ad es.

class Empty { }; 

class Foo 
{ 
    int foo; 
    Empty e; 
}; 

class Bar : private Empty 
{ 
    int foo; 
}; 

Qui, sizeof(Foo) sarà probabilmente 5, ma sizeof(Bar) sarà 4 a causa della classe base vuota.

+0

"lo stesso dell'eredità pubblica, tranne che ...". La differenza è più di questo. Inoltre, gli estranei non possono ad esempio static_cast un 'World *' a 'State *'. –

+0

"sizeof (Foo) sarà probabilmente 5" - o anche peggio 8, se "int" è 4-allineato sulla tua implementazione. –

+0

@Steve Jessp: vero. –

4

In caso

class World: private State 
{ 
}; 

ereditarietà privata significa che tutte public e protected membri State sarebbero ereditati da World e diventerebbero private. Questo sigilla State all'interno di World. Nessuna classe che eredita da World sarà in grado di accedere a qualsiasi funzionalità di State.

0

La parola chiave pubblica/protetta/privata prima del nome della classe di antenato indica la visibilità desiderata dei membri dall'antenato. Con l'ereditarietà privata, il discendente eredita solo l'implementazione dall'antenato, ma non l'interfaccia.

class A { 
public: 
    void foo(); 
}; 

class B : private A { 
public: 
    void bar(); 
}; 

void B::bar() 
{ 
    foo(); // can access foo() 
} 

B b; 
b.foo(); // forbidden 
b.bar(); // allowed 

In generale, si dovrebbe utilizzare l'ereditarietà pubblica perché l'eredità non deve essere utilizzato per l'implementazione solo riutilizzo (che è ciò che l'ereditarietà privata fa).