Java promuove una separazione tra definizioni di classe e implementazioni, come è C ++?


10

Ho un compito da svolgere a casa e devo valutare quale approccio è migliore secondo la "Variazione protetta" di GRASP. Ho trovato una domanda su Stack Overflow sulla separazione dei file di intestazione e di codice in C ++ .

Tuttavia, ciò che voglio sapere perché Java non segue C ++ nel promuovere la separazione tra definizioni di classe e implementazioni di classe. Ci sono dei vantaggi con il metodo Java, rispetto al metodo C ++?


Se vuoi chiedere "Perché Java non usa i file di intestazione", allora chiedi e elimina la roba "che è meglio" - come hai visto, siamo allergici a questo;) Cerca anche, io sono abbastanza sicuro che questo (o almeno domande strettamente correlate) siano state sollevate prima.

oops, il collegamento non ha funzionato. Riformulerò, ciò che volevo sapere sostanzialmente, sono le differenze tra i due e quale si tende a riutilizzare più facilmente il codice o all'estensibilità.
Etienne Noël,

1
C (e per estensione C ++) non aveva altra scelta se non quella di separare i file di intestazione dai file di implementazione, a causa della limitata tecnologia del compilatore one-pass al momento della creazione di C.
Channel72,

2
Java può avere interfacce che possono separare la definizione della classe e l'implementazione della classe, se la classe in questione implementa l'interfaccia. Tuttavia, non è lo stesso di C ++.
FrustratedWithFormsDesigner

1
Inoltre, i file di intestazione C ++ espongono un'implementazione notevolmente maggiore di quella che mi piace, a meno che tu non usi il linguaggio di PIMPL. È necessario elencare tutti i membri dei dati, anche se private, quindi l'implementazione conoscerà le dimensioni e anche le privatefunzioni dei membri.
David Thornley,

Risposte:


13

Quante righe di codice ci sono nel seguente programma?

#include <iostream>

int main()
{
   std::cout << "Hello, world!\n";
   return 0;
}

Probabilmente hai risposto 7 (o 6 se non hai contato la riga vuota o 4 se non hai contato le parentesi graffe).

Il tuo compilatore, tuttavia, vede qualcosa di molto diverso:

~$ cpp hello.cpp | wc
  18736   40822  437015

Sì, questo è 18.7 KLOC solo per un "Ciao, mondo!" programma. Il compilatore C ++ deve analizzare tutto ciò. Questo è uno dei motivi principali per cui la compilazione C ++ richiede così tanto tempo rispetto ad altre lingue e perché i linguaggi moderni evitano i file di intestazione.

Una domanda migliore sarebbe

Perché non C ++ ha file header?

C ++ è stato progettato per essere un superset di C, quindi ha dovuto conservare i file header per la compatibilità con le versioni precedenti.

OK, allora perché C ha i file header?

A causa del suo primitivo modello di compilazione separato. I file oggetto generati dai compilatori C non includono alcuna informazione sul tipo, quindi per evitare errori di tipo è necessario includere queste informazioni nel codice sorgente.

~$ cat sqrtdemo.c 
int main(void)
{
    /* implicit declaration int sqrt(int) */
    double sqrt2 = sqrt(2);
    printf("%f\n", sqrt2);
    return 0;
}

~$ gcc -Wall -ansi -lm -Dsqrt= sqrtdemo.c
sqrtdemo.c: In function main’:
sqrtdemo.c:5:5: warning: implicit declaration of function printf [-Wimplicit-function-declaration]
sqrtdemo.c:5:5: warning: incompatible implicit declaration of built-in function printf [enabled by default]
~$ ./a.out 
2.000000

L'aggiunta delle dichiarazioni del tipo corretto risolve il bug:

~$ cat sqrtdemo.c 
#undef printf
#undef sqrt

int printf(const char*, ...);
double sqrt(double);

int main(void)
{
    double sqrt2 = sqrt(2);
    printf("%f\n", sqrt2);
    return 0;
}

~$ gcc -Wall -ansi -lm sqrtdemo.c
~$ ./a.out 
1.414214

Si noti che non ci sono #includes. Ma quando si utilizza un gran numero di funzioni esterne (che la maggior parte dei programmi farà), dichiararle manualmente diventa noioso e soggetto a errori. È molto più facile usare i file header.

In che modo le lingue moderne sono in grado di evitare i file header?

Utilizzando un diverso formato di file oggetto che include informazioni sul tipo. Ad esempio, il formato file Java * .class include "descrittori" che specificano i tipi di campi e i parametri del metodo.

Questa non era una nuova invenzione. In precedenza (1987), quando Borland aggiunse "unità" compilate separatamente a Turbo Pascal 4.0, scelse di utilizzare un nuovo *.TPUformato anziché Turbo C *.OBJper eliminare la necessità di file di intestazione.


Sebbene sia interessante, sono abbastanza sicuro che potresti impostare Turbo Pascal sui OBJfile di output piuttosto che su TPU...
un CVn del

7

Java ha interfacce per definire un contratto. Ciò fornisce un livello più elevato di astrazione rispetto alle esigenze del chiamante e all'attuazione effettiva. cioè il chiamante non deve conoscere la classe di implementazione, deve solo conoscere il contratto che supporta.

Supponi di voler scrivere un metodo che rallenta tutta la chiave / i valori in una mappa.

public static <K,V> void printMap(Map<K,V> map) {
    for(Entry<K,V> entry: map.entrySet())
        System.out.println(entry);
}

Questo metodo può chiamare entrySet () su un'interfaccia astratta che viene rimossa dalla classe che la implementa. Puoi chiamare questo metodo con.

printMap(new TreeMap());
printMap(new LinkedHashMap());
printMap(new ConcurrentHashMap());
printMap(new ConcurrentSkipListMap());

1
Benvenuti ai programmatori lì Peter - pensavo di riconoscere il nome :-). Aggiungerò che alcune persone sosterranno che anche le classi di base Abstract in Java definiscono un contratto, ma questo è probabilmente un argomento per un thread separato.
Martijn Verburg,

Ciao @MartijnVerburg, bella aggiunta. Penso che le classi astratte offuscino la distinzione tra interfacce senza implementazioni e classi concrete. I metodi di estensione confonderanno ulteriormente la distinzione. Tendo a preferire l'uso di un'interfaccia se posso, poiché sono più semplici.
Peter Lawrey,

Sì, Java inizierà a percorrere il percorso alla Scala di avere diversi modi per definire un contratto pubblico - Non sono sicuro che sia una buona cosa o non ancora :-)
Martijn Verburg

-1 Questo è possibile anche in C ++, con un semplice #define interface class.
Sjoerd,

@Sjoerd Non sapevo che i metodi C ++ non dovessero più usare la virtualparola chiave per ottenere il polimorfismo e questo non ha penalità di prestazione se usi solo uno o due tipi concreti, come se fossero in Java. Puoi indicarmi qualsiasi documentazione su come funziona in C ++?
Peter Lawrey,

5

Le intestazioni esistono, francamente, come un incidente storico. È un sistema incredibilmente povero, nessun'altra lingua ha qualcosa di così terribile e chiunque non debba affrontarli dovrebbe rallegrarsi.


3

Le intestazioni sono lì per consentire la compilazione separata. Includendo # le intestazioni, il compilatore non ha bisogno di sapere nulla sulla struttura binaria del codice C ++ compilato e può lasciare quel lavoro a un linker separato. Java non utilizza un linker separato con il suo compilatore e poiché i file .class sono definiti in modo rigoroso, il compilatore è in grado di leggerli per determinare tutti i loro membri con tutti i loro tipi, senza che sia necessario ri-dichiararli in ogni unità di compilazione.

Puoi includere tutta l'implementazione in un'intestazione C ++, ma fa sì che il compilatore lo ricompili ogni volta che è #incluso, costringendo il linker a riordinare e scartare le copie duplicate.


0

Java promuove la separazione tra definizione della classe e implementazione, dipende solo da dove stai guardando.

Quando sei l'autore di una classe Java, puoi vedere la definizione della classe e la sua implementazione in un file. Questo semplifica il processo di sviluppo poiché devi solo andare in un posto per mantenere la classe, non devi passare tra due file (.h e .cpp come faresti in C ++). Tuttavia, quando sei il consumatore della classe hai a che fare solo con la definizione, tramite un file .class che è impacchettato in un .jar o in un .class autonomo

C ++ ti consente di separare la definizione e l'implementazione, ma è ad-hoc. Ad esempio, non c'è nulla che ti impedisca di scrivere il metodo in linea all'interno del file di intestazione e per le classi di modelli questo è obbligatorio. Il file di intestazione elenca anche tutte le variabili membro, che sono visibili a chiunque guardi il file di intestazione, anche se sono un dettaglio di implementazione della classe e irrilevante per un consumatore.

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.