Perché inseriamo le funzioni dei membri privati nelle intestazioni?

14

La risposta al motivo per cui inseriamo le variabili dei membri privati nelle intestazioni C ++ è che la dimensione della classe deve essere nota nei punti in cui le istanze sono dichiarate in modo che il compilatore possa generare codice che si muove appropriatamente nello stack.

Perché dobbiamo mettere un privato membri nelle intestazioni?

Ma c'è qualche ragione per dichiarare le funzioni private nella definizione della classe?

L'alternativa sarebbe essenzialmente l'idioma di Pimpl ma senza la superfluo indiretta.

Questa funzione del linguaggio è più di un errore storico?

    
posta Praxeolitic 15.05.2014 - 16:48
fonte

6 risposte

10

Le funzioni dei membri privati possono essere virtual e nelle implementazioni comuni di C ++ (che utilizzano un vtable) è necessario conoscere l'ordine e il numero specifico di funzioni virtuali da parte di tutti i client della classe. Questo si applica anche se una o più funzioni membro virtuali sono private .

Potrebbe sembrare che sia come "mettere il carrello davanti ai buoi", perché le scelte di implementazione del compilatore non dovrebbero influenzare le specifiche della lingua. Tuttavia, in realtà il linguaggio C ++ stesso è stato sviluppato nello stesso momento di un'implementazione funzionante ( Cfront ), che utilizzava i vtables.

    
risposta data 15.05.2014 - 22:36
fonte
10

Se hai permesso l'aggiunta di metodi a una classe al di fuori della sua definizione, potrebbero essere aggiunti ovunque , in qualsiasi file, da chiunque.

Ciò darebbe immediatamente a tutti i codici client un accesso banale ai membri dati privati e protetti.

Una volta terminata la definizione della classe, non c'è modo di contrassegnare alcuni file come particolarmente benedetti dall'autore per estenderlo - ci sono solo unità di traduzione piatte. Quindi, l'unico modo ragionevole per dire al compilatore che un particolare insieme di metodi è ufficiale o benedetto dall'autore della classe è quello di dichiararli all'interno della classe.

Si noti che abbiamo accesso diretto alla memoria in C ++, il che significa che è generalmente banale creare un tipo di ombra con lo stesso layout di memoria della classe, aggiungere i miei metodi (o semplicemente rendere pubblici tutti i dati) e reinterpret_cast . Oppure posso trovare il codice della tua funzione privata o smontarlo. Oppure cerca l'indirizzo della funzione nella tabella dei simboli e chiama o direttamente.

Questi specificatori di accesso non tentano di impedire questi attacchi, perché ciò non è possibile. Indicano solo come dovrebbe essere usata una classe.

    
risposta data 15.05.2014 - 17:04
fonte
7

La risposta accettata lo spiega per le funzioni private virtuali , ma risponde solo a uno specifico aspetto della domanda, che è considerevolmente più limitato di quello richiesto dall'OP. Quindi, dobbiamo riformulare: Perché siamo obbligati a dichiarare le funzioni private non-virtuali nelle intestazioni?

Un'altra risposta richiama il fatto che le classi devono essere dichiarate in un blocco, dopo di che sono sigillate e non possono essere aggiunte. Questo è quello che faresti omettendo di dichiarare un metodo privato nell'intestazione, cercando quindi di definirlo altrove. Bel punto. Perché alcuni utenti della classe dovrebbero essere in grado di aumentarlo in un modo che altri utenti non possono osservare? I metodi privati ne fanno parte e non sono esclusi da questo. Ma poi chiedi a perché sono inclusi, e sembra un po 'tautologico. Perché gli utenti di classe devono sapere su di loro? Se non erano visibili, gli utenti non potevano aggiungerne nessuno, e hey presto.

Quindi, volevo fornire una risposta che, piuttosto che includere solo metodi privati per impostazione predefinita, fornisca punti specifici a favore di renderli visibili agli utenti. Un motivo meccanicistico per le funzioni private non virtuali che richiedono la dichiarazione pubblica è fornito in Herb Sutter's GotW # 100 sull'idioma Pimpl come parte di la sua logica. Non parlerò di Pimpl qui, sono sicuro che lo sappiamo tutti. Ma ecco il bit rilevante:

In C++, when anything in a header file class definition changes, all users of that class must be recompiled – even if the only change was to the private class members that the users of the class cannot even access. This is because C++’s build model is based on textual inclusion, and because C++ assumes that callers know two main things about a class that can be affected by private members:

  • Size and Layout: [of members and virtual functions - self-explanatory and great for performance, but not why we're here]
  • Functions: The calling code must be able to resolve calls to member functions of the class, including inaccessible private functions that overload with nonprivate functions — if the private function is a better match, the calling code will fail to compile. (C++ took the deliberate design decision to perform overload resolution before accessibility checking for safety reasons. For example, it was felt that changing the accessibility of a function from private to public shouldn’t change the meaning of legal calling code.)

Sutter è, ovviamente, una fonte estremamente affidabile come membro del Comitato, quindi conosce "una decisione deliberata sul design" quando ne vede uno. E l'idea di richiedere una dichiarazione pubblica di metodi privati come mezzo per evitare una semantica alterata o accessibilità accidentalmente danneggiata in seguito è probabilmente la motivazione più convincente. Per fortuna, l'intera faccenda sembrava piuttosto inutile prima d'ora!

    
risposta data 10.07.2016 - 10:14
fonte
4

Ci sono due ragioni per farlo.

Innanzitutto, renditi conto che lo specificatore di accesso è per il compilatore e non è rilevante in fase di runtime. L'accesso a un membro privato al di fuori dell'ambito è un errore compilazione .

concisione

Considera una funzione che è corta, una o due righe. Esiste per ridurre la replica del codice altrove, che ha anche il vantaggio di essere in grado di cambiare il modo in cui un algoritmo o qualsiasi altra cosa funziona in un posto anziché in molti (ad esempio cambiando un algoritmo di ordinamento).

Preferiresti avere una o due linee veloci nell'intestazione, o avere il prototipo della funzione lì più un'implementazione da qualche parte? È più facile da trovare nell'intestazione e, per le funzioni brevi, è molto più prolisso avere un'implementazione separata.

C'è un altro grande vantaggio, che è ...

Funzioni in linea

Una funzione privata può essere in grado di essere inline, e questo richiede necessariamente che sia nell'intestazione. Considera questo:

class A {
  private:
    inline void myPrivateFunction() {
      ...
    }

  public:
    inline void somePublicFunction() {
      myPrivateFunction();
      ...
    }
};

La funzione privata può essere in grado di essere allineata con la funzione pubblica. Ciò avviene a discrezione del compilatore, poiché la parola chiave inline è tecnicamente un suggerimento , non un requisito.

    
risposta data 15.05.2014 - 17:00
fonte
1

Un altro motivo per avere metodi privati nel file di intestazione: ci sono casi in cui un metodo inline pubblico non fa molto di più che chiamare uno o più metodi privati. Avere i metodi privati nell'intestazione significa che una chiamata al metodo pubblico può essere completamente allineata al codice effettivo dei metodi privati, e l'inlining non si ferma a una chiamata al metodo privato. Anche da una diversa unità di compilazione (e i metodi pubblici dovrebbero di solito essere chiamati da diverse unità di compilazione).

Naturalmente c'è anche la ragione che il compilatore non può rilevare problemi con la risoluzione di sovraccarico se non conosce tutti i metodi, compresi quelli privati.

    
risposta data 10.07.2016 - 23:20
fonte
0

Consentire a tali funzioni di accedere ai membri privati. Altrimenti dovresti comunque friend nell'intestazione.

Se una qualsiasi funzione potesse accedere ai membri privati della classe, il privato sarebbe inutile.

    
risposta data 15.05.2014 - 16:58
fonte

Leggi altre domande sui tag