2014-09-18 14 views
5

Ho lavorato in C e C++ e quando si tratta di gestione dei file mi confondo. Lasciatemi dire le cose che conosco.Gestione file C vs C++

In C, usiamo funzioni:

  • fopen, fclose, fwrite, fread, ftell, fseek, fprintf, fscanf, feof, Fileno, fgets, fputs, fgetc, fputc.
  • FILE * fp per il puntatore del file.
  • Modi come r, w, a

so quando utilizzare queste funzioni (Spero non ha perso nulla di importante).

In C++, usiamo funzioni/operatori:

  • fstream f
  • f.open, f.close, f >>, f < <, f.seekg, f.seekp, f. tellg, f.tellp, f.read, f.write, f.eof.
  • Modi come ios :: in, ios :: out, ios :: bin, ecc ...

così è possibile (consigliato) per utilizzare le operazioni di file compatibili C in C++? Quale è più ampiamente utilizzato e perché? C'è qualcosa di diverso da questi di cui dovrei essere a conoscenza?

risposta

10

A volte c'è il codice esistente che si aspetta l'uno o l'altro con cui è necessario interagire, il che può influenzare la scelta, ma in generale le versioni C++ non sarebbero state introdotte se non ci fossero problemi con le versioni C che potrebbe risolvere. I miglioramenti includono:

  • semantica RAII, che significa ad es. fstream s chiudono i file che gestiscono quando lasciano portata

  • capacità modale di lanciare eccezioni quando si verificano errori, che può rendere il codice più pulito incentrata sulla tipica lavorazione/successo (vedi http://en.cppreference.com/w/cpp/io/basic_ios/exceptions per la funzione API ed esempio)

  • sicurezza tipo, in modo tale che come vengono effettuate ingresso e uscita è implicitamente selezionato utilizzando il tipo di variabile coinvolta

    • C-stile I/O ha un potenziale di incidenti: es int my_int = 32; printf("%s", my_int);, dove %s indica a printf di aspettarsi un puntatore a un buffer di caratteri ASCIIZ ma invece viene visualizzato my_int; in primo luogo, la convenzione passaggio degli argomenti può significare int s sono passati diverso a const char* s, secondo sizeof int potrebbe non corrispondere sizeof const char*, infine, anche se printf estratti 32 come const char*al meglio sarà solo stampare caratteri casuali da indirizzo di memoria 32 in poi finché non raggiunge casualmente un carattere NUL - molto più probabilmente il processo mancherà di autorizzazioni per leggere parte di quella memoria e il programma si bloccherà. I compilatori C moderni possono a volte convalidare la stringa di formato rispetto agli argomenti forniti, riducendo questo rischio.
  • estensibilità per i tipi definiti dall'utente (ad es.puoi insegnare agli stream come gestire le tue classi)

  • supporto per il dimensionamento dinamico delle stringhe riceventi in base all'ingresso effettivo, mentre le funzioni C tendono ad avere dimensioni e buffer del buffer massimi codificati nel codice utente per assemblare dimensioni arbitrarie ingresso

flussi sono anche talvolta criticato per:

  • verbosità di formattazione, in particolare "manipolatori io" impostazione lunghezza, precisione, base, padding, rispetto al printf stringhe di formato -style

  • un mix a volte confuso di manipolatori che persistono le loro impostazioni su più operazioni I/O e altri che vengono ripristinati dopo ogni operazione

  • mancanza di classe convenienza per RAII spingere/risparmio e successivamente popping/ripristino dello stato manipolatore

  • essere lento, come commenti e documenti here

+0

Grazie per la rapida risposta Tony. Non ho capito la parte di sicurezza del tipo. In C abbiamo fprintf, giusto? Si prega di approfondire il potenziale incidente. – 0aslam0

+0

Si prega inoltre di elaborare eccezioni generate da iostreams. –

+3

Un'altra critica agli iostreams è che [sono miseramente lenti] (http://stackoverflow.com/q/4340396/103167) –

3

le differenze di prestazioni tra Ben Voigt printf()/fwrite La formattazione degli I/O in stile I/O e C++ IO dipende molto dall'implementazione.

Alcune implementazioni (Visual C++ per esempio), creano i loro flussi di I/O su oggetti FILE * e questo tende ad aumentare la complessità di esecuzione della loro implementazione. Si noti, tuttavia, che non vi era alcun vincolo particolare per implementare la libreria in questo modo.

Nel mio parere, i vantaggi di C++ I/O sono i seguenti:

  • di sicurezza di tipo.
  • Flessibilità di implementazione. Il codice può essere scritto per eseguire formattazioni o input specifici da o verso un oggetto generico ostream o istream. L'applicazione può quindi richiamare questo codice con qualsiasi tipo di oggetto stream derivato. Se il codice che ho scritto e testato su un file ora deve essere applicato a un socket, una porta seriale o qualche altro tipo di flusso interno, è possibile creare un'implementazione di flusso specifica per quel tipo di I/O. Estendere l'I/O in stile C in questo modo non è nemmeno vicino al possibile.
  • Flessibilità nelle impostazioni internazionali: L'approccio C di utilizzare un singolo locale globale è, a mio parere, gravemente imperfetto. Ho riscontrato casi in cui ho richiamato codice libreria (una DLL) che ha cambiato le impostazioni internazionali globali sotto il mio codice e ha completamente incasinato il mio output. Un flusso C++ consente di eseguire imbue() qualsiasi locale su un oggetto flusso.
+0

Niente batte la conoscenza acquisita dall'esperienza. – 0aslam0

+0

Sebbene l'implementazione conti certamente, l'I/O C++ è richiesto (per specifiche) per passare attraverso una catena di almeno quattro funzioni virtuali (una nel flusso, una nel buffer una in locale e una in un facet) che C i/o non è richiesto di essere. Questa catena di riferimenti indiretti deve essere presente anche se non strettamente richiesta dal task, rendendo così il C++ più lento del C i/o deve essere. Quando sembrano uguali, è perché il tempo speso nel trasferimento dei dati è parallelizzato rispetto al tempo trascorso per convertirli in forma testuale (che è diversa da C e C++) e più lento di entrambi loro –

0

Un interessante confronto critico può essere trovato qui.

C++ FQA io

Non esattamente educato, ma fa pensare ...

responsabilità

L'++ FQA C (che è una risposta critica alle FAQ C++) è spesso considerato dalla C++ comunità di uno "scherzo stupido rilasciato da un ragazzo stupido l'ancora non capisco che cosa C++ è o vuole essere "(cit. dal FQA stesso).

Questo tipo di argomentazione vengono spesso utilizzati per la fiamma (o sfuggire) battaglie di religione tra C credenti ++, Altre lingue credenti o atei lingua ciascuno a suo modesto parere convinto di essere in qualcosa di superiore al altro.

Non sono interessato a queste battaglie, mi piace solo stimolare il ragionamento critico sui pro e contro le argomentazioni. Il C++ FQA - in questo senso - ha il vantaggio di posizionare sia l'FQA che le FAQ uno sull'altro, consentendo un confronto immediato. E questo è l'unico motivo per cui l'ho fatto riferimento allo.

A seguito di commenti TonyD, di seguito (serbatoi per loro, mi chiarisco la mia intenzione necessitano di un chiarimento ...), si deve notare che l'OP non sta solo discutendo il << e >> (ne parlo solo in i miei commenti solo per brevità) ma l'intero set di funzioni che costituisce il modello I/O di C e C++.

Con questa idea in mente, pensa anche ad altri linguaggi "imperativi" (Java, Python, D ...) e vedrai che sono tutti più conformi al modello C rispetto al C++. A volte lo rende persino sicuro (ciò che il modello C non è, e questo è il suo principale svantaggio).

Che il mio punto è tutto

Al momento C++ è venuto avanti come corrente principale (1996 o giù di lì) il <iostream.h> libreria (notare il ".h": pre-ISO) era in una lingua in cui i modelli dove non ancora completamente disponibile, e, in sostanza, nessun supporto sicuro per le funzioni varadic (dobbiamo attendere fino a C++ 11 per ottenerle), ma con funzioni sovraccarico sicuro tipo.

L'idea di ricaricare il parametro << ridefinendo il suo primo parametro più e più volte è, in effetti, un modo per concatenare una serie variabile di argomenti utilizzando solo una funzione binaria, che può essere sovraccaricata in un modo sicuro. Questa idea si estende a qualsiasi "funzione di gestione dello stato" (come width() o precision()) tramite manipolatori (come setw) appare come una conseguenza naturale. Questo punto, a parte ciò che puoi dire all'autore FQA, sono fatti reali. Ed è anche un dato di fatto che FQA è l'unico sito che ho trovato che ne parla.

Detto questo, anni più tardi, quando la lingua D è stato progettato partendo offrendo modelli varadic, la funzione writef è stato aggiunto nella libreria standard D che fornisce una sintassi printf -come, ma anche di essere perfettamente type-safe. (vedi here)

Al giorno d'oggi il C++ 11 ha anche modelli varadici ... quindi lo stesso approccio può essere messo in atto nello stesso modo.

Morale della favola

Sia C++ e modelli IO C appaiono rispetto "obsoleti" per uno stile moderno programmazione. C mantenimento della velocità, sicurezza del tipo C++ e "astrazione più flessibile per la localizzazione" (ma mi chiedo quanti programmatori C++ sono al mondo che sono a conoscenza di localizzazioni e sfaccettature ...) a un costo di runtime (jut track con un debugger il < di un numero, passando per lo stream, il buffer locale e il facet ... e tutte le relative funzioni virtuali!).

Il modello C, è anche facilmente estensibile ai messaggi parametrici (quello l'ordine dei parametri dipende dalla localizzazione del testo sono in) con stringhe di formato come

@ 1% d @ 2% i permettendo di scrostare come "text @2%i text @1%d ..."

Il modello C++ non ha il concetto di "stringa di formato": l'ordine dei parametri è fisso ed è identificato con il testo.

Ma C++ 11 modelli varadic possono essere utilizzati per fornire un supporto che:

  • in grado di offrire sia compile-time e run-time selezione locale
  • in grado di offrire sia a tempo di compilazione e di run- L'ordine parametrico
  • può offrire il tipo di parametro in fase di compilazione sicurezza
  • ... tutto utilizzando una semplice metodologia di stringa di formato.

È tempo di standardizzare un nuovo modello di I/O C++?

+0

"ma fa riflettere. .. "- sì, mostra come qualcuno con una ragionevole conoscenza di una forte avversione per il C++ possa scrivere FQA deliberatamente miopi e incompleti e crogiolarsi nella" fama "dei principianti adoranti. –

+0

@TonyD: quale parte di "Non esattamente educato" non era chiara? Mi sembra che tu stia commettendo l'errore opposto. Molti argomenti sul modo C++ di usare l'iniezione degli operatori << sono comuni: il C++ è l'unico linguaggio che gestisce l'IO in questo modo. E la ragione è che quando iostream è stato progettato in C++ non sono disponibili modelli e (di conseguenza) varadic templats, necessari per implementare strutture di tipo printf sicure. Basta dare un'occhiata a D's 'std.write' /' std.read'. Possono essere portati in C++ 11 in modo quasi banale, ma in C++ 96 (qualunque cosa tu voglia chiamare pre-iso) .... –

+0

"quale parte di" Non esattamente educato "non era chiara?" - Non ho menzionato * nulla sulla cortesia * - sono gli argomenti tecnici che sono in modo schiacciante pateticamente e spesso deliberatamente miopi. E 'cout << a << b' vs' cout (a, b) 'è l'argomento di circa l'1% del contenuto FQA che linki appena menzionato in 15.4, che ritorna a un più confortevole fruscio della vecchia 'operator void *' vs. 'bool' (risolto in C++ 11). –