Perché le variabili private sono descritte nel file di intestazione accessibile al pubblico?


11

OK, quindi spero che questa sia una domanda abbastanza soggettiva per i programmatori, ma qui va. Allargo continuamente la mia conoscenza delle lingue e delle pratiche di ingegneria del software ... e mi sono imbattuto in qualcosa che per me non ha assolutamente senso.

In C ++, le dichiarazioni di classe includono private:metodi e parametri nel file di intestazione, che, in teoria, è ciò che passi all'utente da includere se le rendi lib.

In Objective-C, @interfacefacciamo praticamente la stessa cosa, costringendoti a elencare i tuoi membri privati ​​(almeno, c'è un modo per ottenere metodi privati ​​nel file di implementazione).

Da quello che posso dire, Java e C # consentono di fornire un'interfaccia / protocollo che può dichiarare tutte le proprietà / i metodi accessibili al pubblico e offre al programmatore la possibilità di nascondere tutti i dettagli dell'implementazione nel file di implementazione.

Perché? L'incapsulamento è uno dei principi principali di OOP, perché C ++ e Obj-C mancano di questa capacità di base? Esiste una sorta di soluzione ottimale per Obj-C o C ++ che nasconde tutta l' implementazione?

Grazie,


4
Condivido il tuo dolore. Sono scappato urlando dal C ++ la prima volta che ho aggiunto un campo privato a una classe e ho dovuto ricompilare tutto ciò che lo utilizzava.
Larry Coleman,

@Larry, non mi dispiace troppo, ma sembra essere annunciato come un ottimo linguaggio OO, ma non può nemmeno incapsularsi "correttamente".
Stephen Furlani,

2
Non credere all'hype. Esistono modi migliori per eseguire OO, sia nei campi di battitura statici che dinamici.
Larry Coleman,

In un certo senso è un ottimo linguaggio, ma non a causa delle sue abilità OO, che sono un innesto di Simula 67 su C.
David Thornley,

Dovresti fare un po 'di programmazione C. Quindi avrai una migliore comprensione del perché queste lingue sono come sono, incluso Java C # ecc.
Henry,

Risposte:


6

La domanda è se il compilatore deve sapere quanto è grande un oggetto. In tal caso, il compilatore deve conoscere i membri privati ​​per poterli contare.

In Java, ci sono tipi e oggetti primitivi e tutti gli oggetti sono allocati separatamente e le variabili che li contengono sono in realtà puntatori. Pertanto, poiché un puntatore è un oggetto a dimensione fissa, il compilatore sa quanto è grande una variabile, senza conoscere la dimensione effettiva dell'oggetto puntato. Il costruttore gestisce tutto ciò.

In C ++, è possibile avere oggetti rappresentati localmente o nell'heap. Pertanto, il compilatore deve sapere quanto è grande un oggetto, in modo da poter allocare una variabile o matrice locale.

A volte è desiderabile dividere la funzionalità della classe in un'interfaccia pubblica e in qualsiasi altra cosa privata, ed è qui che entra in gioco la tecnica PIMPL (Pointer to IMPLementation). La classe avrà un puntatore a una classe di implementazione privata e i metodi della classe pubblica possono chiamare quello.


il compilatore non può consultare la libreria di oggetti per ottenere queste informazioni? Perché queste informazioni non possono essere leggibili automaticamente invece che leggibili dall'uomo?
Stephen Furlani,

@Stephen: al momento della compilazione, non esiste necessariamente una libreria di oggetti. Poiché la dimensione degli oggetti influisce sul codice compilato, deve essere noto al momento della compilazione, non solo al momento del collegamento. Inoltre, è possibile per Ah definire la classe A, Bh per definire la classe B e le definizioni delle funzioni in A.cpp per usare oggetti di classe B e viceversa. In tal caso, sarebbe necessario compilare ciascuno di A.cpp e B.cpp prima dell'altro, se si prendono le dimensioni dell'oggetto dal codice oggetto.
David Thornley,

non è possibile eseguire il collegamento durante la compilazione? Confesso di non conoscere i dettagli a quella profondità, ma se l'interfaccia di un oggetto espone i suoi contenuti, questi stessi contenuti non possono essere esposti da una libreria o altro componente leggibile dalla macchina? devono essere definiti in un file di intestazione accessibile pubblicamente? Capisco che questo faccia parte della maggior parte delle lingue C, ma deve essere così?
Stephen Furlani,

1
@Stephen: il collegamento può essere eseguito durante la compilazione solo se sono presenti tutti i componenti appropriati. Anche allora, sarebbe un processo complicato, che prevede una compilazione parziale (tutto tranne le dimensioni dell'oggetto), un collegamento parziale (per ottenere le dimensioni dell'oggetto), quindi una compilazione e un collegamento finali. Dato che C e C ++ sono linguaggi relativamente vecchi, questo non sarebbe successo. Presumibilmente qualcuno potrebbe progettare un nuovo linguaggio senza file header, ma non sarebbe C ++.
David Thornley,

14

A causa della progettazione di C ++, per creare un oggetto nello stack il compilatore deve sapere quanto è grande. Per fare ciò deve avere tutti i campi presenti nel file di intestazione, poiché è tutto ciò che il compilatore può vedere quando l'intestazione è inclusa.

Ad esempio se si definisce una classe

class Foo {
    public int a;
    private int b;
};

allora sizeof(Foo)è sizeof(a) + sizeof(b). Se esistesse un meccanismo per separare i campi privati, l'intestazione potrebbe contenere

class Foo {
    public int a;
};

con sizeof(Foo) = sizeof(a) + ???.

Se vuoi davvero nascondere i dati privati, prova il linguaggio del pimpl, con

class FooImpl;
class Foo {
    private FooImpl* impl;
}

nell'intestazione e una definizione FooImplsolo per Fooil file di implementazione.


Oh. Non avevo idea che fosse così. È per questo che linguaggi come Java, C # e Obj-C hanno "Oggetti di classe" in modo che possano chiedere alla classe quanto deve essere grande l'oggetto?
Stephen Furlani,

4
Prova a usare un pimpl, un puntatore a un'implementazione privata. Poiché tutti i puntatori di classe hanno le stesse dimensioni, non è necessario definire la classe di implementazione, dichiararla soltanto.
Scott Wales,

Penso che dovrebbe essere una risposta anziché un commento.
Larry Coleman,

1

Tutto questo dipende dalla scelta del design.

Se desideri veramente nascondere i dettagli dell'implementazione privata della classe C ++ o Objective-C, allora fornisci una o più interfacce supportate dalla classe (classe virtuale pura C ++, protocollo Objective-C) e / o crea la classe in grado di costruirsi da solo fornendo un metodo factory statico o un oggetto factory di classe.

Il motivo per cui le variabili private sono esposte nel file di intestazione / dichiarazione di classe / interfaccia @ è che un consumatore della tua classe potrebbe aver bisogno di crearne una nuova istanza e un new MyClass()o [[MyClass alloc]init]nel codice client necessita del compilatore per capire quanto sia grande una MyClass oggetto è per fare l'allocazione.

Java e C # hanno anche le loro variabili private dettagliate nella classe - non fanno eccezione, ma IMO il paradigma dell'interfaccia è molto più comune con quei linguaggi. Potresti non avere il codice sorgente in ogni caso, ma ci sono abbastanza metadati nel codice compilato / byte per dedurre queste informazioni. Poiché C ++ e Objective-C non dispongono di questi metadati, l'unica opzione sono i dettagli effettivi dell'interfaccia class / @. Nel mondo COM C ++, non si espongono le variabili private di nessuna classe, tuttavia è possibile fornire il file di intestazione, poiché la classe è pura virtuale. Un oggetto factory di classe è registrato per creare anche le istanze effettive e ci sono alcuni metadati in varie forme.

In C ++ e Objective-C, è meno difficile distribuire il file di intestazione rispetto alla scrittura e alla gestione di un file di interfaccia / protocollo aggiuntivo. Questo è uno dei motivi per cui l'implementazione privata è esposta così spesso.

Un altro motivo in C ++ è rappresentato dai modelli: il compilatore deve conoscere i dettagli della classe per generare una versione di quella classe specializzata nei parametri forniti. La dimensione dei membri della classe varierebbe con la parametrizzazione, quindi deve avere queste informazioni.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.