2010-04-30 19 views
8

Como todos saben, hay bibliotecas que usan flujos como iostream y fstream.¿Por qué las transmisiones en C++?

Mi pregunta es:

  • Por qué corrientes? ¿Por qué no se quedaron con funciones similares a print, fgets y así sucesivamente (por ejemplo)?

Se requieren sus propios operadores << y >> pero lo único que hacen podrían implementarse en funciones simples como la de arriba, también la función

printf("Hello World!"); 

es mucho más fácil de leer y lógico para mí que

cout << "Hello World"; 

También creo que todas esas abstracciones de cadenas en C++ compilan todas (llamadas de función estándar menos eficientes) en binario.

+2

La eficiencia no es realmente una preocupación, ya que el 99% del tiempo se gastará en las mismas llamadas al sistema operativo para imprimir en la consola –

+2

¿Qué abstracciones de cadena? –

+2

-1 hey boy, supongo que eres un principiante en el mundo C++. ¿Cómo puede decir que es menos eficiente si no lo sabe? Antes de la crítica, por favor, aprenda y sea modesto. –

Respuesta

24

Las corrientes tienen un mejor tipo de seguridad.Por ejemplo, printf("%s", a); puede salir terriblemente mal si a es un número entero. cout << a; no tiene este problema.

Otro problema es que las transmisiones se ajustan mejor a las metodologías de diseño orientadas a objetos.

Por ejemplo, tiene una aplicación simple que escribe algunos resultados y luego desea que la salida vaya a un archivo en lugar de a la consola. Con las llamadas C, deberá reemplazar todas las llamadas a printf para llamar al fprintf y tener cuidado de mantener el FILE* en el camino. Con corrientes que acaba de cambiar la clase concreta de la corriente que está utilizando y eso es todo, la mayor parte del código sigue siendo el mismo de esta manera:

void doSomething(ostream& output) 
{ 
    output << "this and that" << a; 
} 

doSomething(cout); 
doSomething(ofstream("c:\file.txt")); 
+1

Hah" horriblemente mal ", tan cierto, tan cierto. –

+0

Por supuesto, los compiladores modernos se han extendido (no de forma compatible con los estándares) para admitir la verificación de tipos de cadenas de formato * printf. –

+0

"Con llamadas a C, tendrá que reemplazar todas las llamadas a printf por llamadas a fprintf": la situación no es tan mala, p. puede hacer la redirección de stream freopen ("output.txt", "w", stdout); y evite el reemplazo – Tebe

7

Las transmisiones en C++ son seguras. En C, si dices:

double d = 1.23; 
printf("%d", d); 

no se garantiza que recibas un mensaje de error, aunque la conversión sea incorrecta.

Parece que hace preguntas muy básicas de C++. ¿Qué libro de texto C++ estás usando que no los cubra?

2

corrientes se pueden encadenar

cout << "hello" << " " << "world" 
+1

Entonces, podrían ser métodos: out.print ("hello"). Print ("") .print ("world") –

+2

'std :: cout.operator << (" hello "). Operator << (" ") .operator << ("world"); ' –

5

printf no es un tipo seguro para uno. La interfaz de cout también es más genérica, lo que permite que muchas cosas no estén disponibles con la versión de printf. Un buen ejemplo es que su operador de flujo para sus tipos es, si lo está haciendo bien, refiriéndose a std :: ostream como la transmisión, no la transmisión. Por lo tanto, puede cambiar el destino de la salida simplemente utilizando una secuencia diferente. Para hacer eso con printf, tiene que hacer una gran cantidad de sobrescritura dependiente de la plataforma de los identificadores de salida.

También creo que todas esas abstracciones de cadena en C++ compilan todas hasta llamadas de función estándar (menos eficientes) en binario.

Piensa todo lo que quieras. Hasta que no pruebes tu suposición, tu opinión no tendrá mucho peso. Además, debe considerar la pérdida de abstracción, algo que suele ser mucho más importante en el desarrollo de software moderno.

1

La corriente como la sintaxis con operator<< y operator>> permite una buena concatenación.

printf("%d%d%d%d%d", a, b, c, d, e); 

vs

cout << a << b << c << d << e; 

Por otra parte, en el primer enfoque siempre hay que pensar en el tipo, mientras que en el enfoque de la corriente, el tipo no debe especificarse.

1

En realidad

cout << "Test: " << test << endl; 

parece mucho más intuitivo yo que

printf("Test: %d\n", test); 

Si nunca parece un printf antes, no hay manera de que usted sepa lo que hace.

En cualquiera de los casos,

print "Test: " + test 

(en varios idiomas, incluyendo Python :() hace mucho más sentido :)

+0

Python tiene una prueba de 'impresión':% d "% test' también, y es muy necesario para un formateo más complicado. – UncleBens

+0

@UncleBens: Es cierto, y es muy desafortunado que eligieron usar cadenas de formato estilo C, * especialmente * que requieren el tipeo para un lenguaje dinámico ... –

+0

Si nunca antes viste las transmisiones en C++, es aún peor: crees que están realizando algunos cambios enloquecidos! –

9

Por un lado, le permite tomar ventaja de el modelo de objetos C++ para crear funciones que no les importa si están escribiendo en una salida estándar, un archivo o un socket de red (si tiene un socket de red derivado de ostream). P.ej.

void outputFoo(std::ostream& os) 
{ 
    os << "Foo!"; 
} 

int main() 
{ 
    outputFoo(std::cout); 

    std::ofstream outputFile("foo.txt"); 
    outputFoo(outputFile); 

    MyNetworkStream outputSocket; 
    outputFoo(outputSocket); 
} 

Y de manera similar para los flujos de entrada.

Las transmisiones también tienen una ventaja cuando se trata de ingresar y generar objetos. ¿Qué pasaría si quisiera leer en un objeto con scanf?

MyObject obj; 
scanf("??", &obj); // What specifier to use? 

Incluso si hubiera un especificador adecuada, ¿cómo scanf saben cómo rellenar los miembros del objeto? Con flujos de C++, puede sobrecargar operator<< y escribir

MyObject obj; 
std::cin >> obj; 

y funcionará. De forma similar para std::cout << obj, para que pueda escribir el código de serialización de objetos en un solo lugar y no tener que preocuparse por eso en ningún otro lado.

+0

Un socket de red que se deriva de 'ostream' es realmente algo que me gustaría ver :) – shoosh

+5

@shoosh' boost :: asio :: ip :: tcp :: iostream'? – Cubbi

1

C++ le permite usar printf, por cierto. Entonces, si te gusta eso, adelante y úsalo.

Las transmisiones se utilizan para mucho más que escribir salidas en la consola. Son una solución de búfer de datos de propósito general que se puede aplicar a todo, desde la salida de la pantalla hasta el manejo de archivos, el tráfico de la red y las interfaces de los dispositivos de entrada. Tus cosas pueden escribir en (o leer de) secuencias sin importar a dónde irán realmente esos datos.

Supongamos que tiene un objeto complejo que desea poder escribir en la consola de salida, en un archivo de registro y en una ventana emergente de depuración. ¿Vas a escribir tres funciones que hacen casi exactamente lo mismo, o vas a escribir una función a la que pasas un flujo de salida (y)?

2

Otro de los beneficios de las transmisiones es que se hicieron extensibles. Con corrientes, usted puede tener sus clases definidas por el usuario funcionan igual que construyen en tipos:

class foo { ... }; 

ostream &operator<<(ostream &ostr, const foo &f) 
{ 
    ostr << ... how you want to print a foo ...; 
    return ostr; 
} 

Y ahora puede imprimir una foo como cualquier otra cosa:

int n = ... 
foo f = ... 

cout << n << ": " << f << endl; 
0

todavía uso printf, principalmente porque es fácil controlar el formato de salida. Para mí, la seguridad de tipo no es un beneficio principal aquí porque se puede detectar fácilmente mediante prueba, dado que un programa basado en consola es mucho más fácil de probar que las aplicaciones basadas en la UI o basadas en la web. Si no realiza la prueba, los errores más graves pueden superar el tiempo de compilación de cualquier forma.

Tampoco estoy de acuerdo con otra razón por la que el flujo de notificaciones sea más flexible debido a la capacidad de intercambio. Es equivalente a recomendar el uso de fprintf (fout, ...) para intercambiabilidad. Si necesita redirigir la salida, use tubería. Si está en una biblioteca, ¿por qué no devuelve una cadena y deja que la persona que llama decida dónde va?

+0

Use un tubo: no todo es un comando como la aplicación. Devuelve una cuerda, no muy eficiente. – shoosh

+1

shoosh, solo por curiosidad, ¿cómo puedes devolver una cadena en C desde la función? Quiero decir, si se asignó allí como (static char * mystring), solo devolverías un puntero. si te refieres a una cadena de AWL, solo devuelves el objeto que representa una cadena, la cadena se retiene en otro lugar - no pierde efectividad. – Tebe

3

Además de ser tipo seguro y polimórfico, las transmisiones son más portátiles. En algunos sistemas printf con un largo requiere "% d" y en algunos sistemas requiere "% ld".

+0

El cumplimiento siempre requiere '% ld', pero el' l' no hace nada en algunas plataformas. – Potatoswatter

+0

¡He cambiado un montón de código complicado y loco para solucionar este problema al usar streams sin siquiera darme cuenta! –

+0

+1 un día me perdí por mucho tiempo hasta que usé el depurador para mirar – Tebe

0

stringstream es más seguro que snprintf/sscanf porque su totalidad evita la posibilidad de desbordamiento de búfer (incluso los "agraciados fallos").

0

Las secuencias funcionan con plantillas, mientras que printf/scanf no.

2

C++ IOStreams son ridículamente ineficaces (en la mayoría de las implementaciones que conozco). A menudo, eso no es una preocupación, pero cuando es, la biblioteca es básicamente inútil. También es un buen punto que la sintaxis no es intuitiva (y muy, muy prolija). La biblioteca es compleja e innecesariamente difícil de ampliar. Tampoco es muy flexible. Cuando se compara con algo como el STL, IOStreams realmente parece un mal sueño. Pero está aquí, y estamos atrapados con eso.

La razón por la que está aquí, y la razón por la que parece que lo hace, es que se desarrolló antes, antes de que C++ fuera un lenguaje maduro. Antes teníamos décadas de experiencia diciéndonos qué es y qué no es un buen diseño de la biblioteca. Antes de que alguien realmente supiera cuáles eran las opciones.

C++ necesitaba una biblioteca de E/S que fuera mejor que C. Y en algunas formas importantes, C++ IOStreams es mejor. Son tipogibles y extensibles, como otros han mencionado. Al implementar un solo operador, puedo imprimir una clase definida por el usuario. Eso no se pudo hacer con printf. Tampoco tengo que preocuparme por equivocarme con los especificadores de formato e imprimir basura debido a la falta de seguridad del tipo.

Esos elementos necesarios por arreglar. Y bueno, en los primeros días, las funciones virtuales y la sobrecarga del operador eran la mierda. Se veía genial.Por supuesto, las bibliotecas querían usar esas características.

La biblioteca iostreams es un compromiso entre:

  • algo más seguro y más extensible que de C stdio.h
  • algo eficiente
  • algo bien diseñado e intuitivo
  • una biblioteca que en realidad existía en el momento en que C++ se estaba estandarizando. (Tuvieron que añadir algo , por lo que tenían que elegir entre los candidatos que realmente existían en el momento.)

La biblioteca no logra todo esto, y creo que hoy en día, con nuestros décadas de experiencia con el lenguaje, podríamos haber diseñado una biblioteca mucho mejor. Pero a mediados de los 90, cuando buscaban una biblioteca de E/S para agregar, era lo mejor que podían encontrar.

Cuestiones relacionadas