2012-05-21 12 views
6
class Person { 
private: 
    string firstName; 
    string lastName; 
public: 
    Person() {} 

    Person(ifstream &fin) { 
     fin >> firstName >> lastName; 
    } 

    void print() { 
     cout << firstName 
      << " " 
      << lastName 
      << endl; 
    } 
}; 

int main() { 
    vector<Person> v; 
    ifstream fin("people.txt"); 

    while (true) { 
     Person p(fin); 
     if (fin == NULL) { break; } 
     v.push_back(p); 
    } 

    for (size_t i = 0; i < v.size(); i++) { 
     v[i].print(); 
    } 

    fin.close(); 
    return 0; 
} 

¿Puede explicarme cómo funciona el siguiente fragmento de código? if (fin == NULL) {break; }C++, leyendo un archivo usando ifstream

fin es un objeto en la pila, no un puntero, por lo que no puede convertirse en NULL. No pude encontrar operador sobrecargado == función en la clase ifstream. Así que no puedo entender cómo funciona este fragmento.

Respuesta

3

Las clases base de istream y ostream tienen funciones de conversión implícita , que les permiten ser utilizadas como un valor booleano; en pre-C++ 11, , la conversión implícita era void*.

Nunca fue la intención de que el resultado de esta conversión puede utilizar como un puntero , y un código como fin == NULL muestra una extremadamente pobre comprensión de C++ y los flujos estándares. La forma idiomática de escribir el primer bucle sería definir un constructor por defecto y un operator>> para Person, y luego escribir:

Person p; 
while (fin >> p) { 
    v.push_back(p); 
} 

(Y ya que estoy en ello: en realidad se debe probar el valor de retorno de fin.close(), y no volver 0 si falla:.

fin.close(); 
return fin ? EXIT_SUCCESS : EXIT_FAILURE; 

)

+0

Aunque estoy de acuerdo en que probablemente sea una buena práctica hacerlo, nunca ** he visto código que pruebe si el cierre fue exitoso y ajuste el valor de retorno. Personalmente, nunca llamo 'cerrar' de todos modos, confiando en RAII en su lugar, pero afortunadamente puedo salirse con la suya escribiendo código que no hace IO robusto. –

+0

@KonradRudolph: Supongo que confiar en RAII implica que el destructor no prueba si el cierre fue exitoso tampoco. No hay medios para transportar dicha condición de error a la persona que llama (a menos que recurra a variables globales). –

+0

@Frerich Por supuesto. Como dije, puedo hacer IO no robusto. Lazy, lo sé, pero hace que el código sea mucho más simple y más limpio. –

2

Ésta no es la forma en st resmas se supone que se utilizan. Es cierto, esto (¡por desgracia!) Compila e incluso hace lo "correcto". Pero no escriba código así. Quien escribió este código probablemente pensó que era inteligente.

Pero lo que realmente hicieron fue romper las expectativas de los programadores de C++ al introducir una API nueva y poco convencional, sin ventajas reales.

Este código inicializa un objeto del tipo Person a partir de un flujo de entrada. Desafortunadamente, al hacer esto, el código pierde la oportunidad de probar errores mientras lee el objeto. Esto no es bueno Un objeto debe no tener un constructor que acepta una secuencia de entrada, debe sobrecargar operator>>.

+0

Jugando al defensor de los demonios aquí: creo que transmitir un flujo a un constructor tiene mucho sentido. Puede usar excepciones para señalar errores y evitar el problema de tener la posibilidad de objetos no inicializados (a diferencia de su alternativa sugerida, que permite construir un objeto pero no usar 'operator >>'). –

+0

@Frerich En principio, estoy totalmente de acuerdo con usted. También es por eso que dije que era un programador que era inteligente, en lugar de James, que pensó que era alguien que no tenía idea de las transmisiones. Pero todavía rompe expectativas y probablemente no sea una buena idea en la construcción de C++ en la parte superior de la biblioteca de flujo existente. Los flujos se podrían hacer mucho mejor, pero luego construir una biblioteca propia, no construir sobre un patrón existente que funciona de manera diferente. –