2012-05-21 58 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; 
} 

请你解释一下,下面的代码片段如何工作? if(fin == NULL){break; }C++,使用ifstream读取文件

fin是堆栈上的一个对象,而不是一个指针,所以它不能成为NULL。 我无法在ifstream类中找到重载的运算符==函数。 所以我不明白这段代码如何工作。

回答

3

istreamostream的基类具有隐式转换 函数,它们允许它们用作布尔值;在pre-C++ 11, 中隐式转换为void*

这个转换的结果永远不会被用作 指针,类似fin == NULL这样的代码对C++和标准流的理解非常差。的 写的第一循环中的惯用方法是定义一个默认构造函数和 operator>>Person,然后写:

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

(虽然我吧:你真的应该测试返回值的 fin.close(),而不是如果失败返回0

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

+0

尽管我同意这可能是最好的做法,但我从未见过**代码,它测试关闭是否成功,并调整返回值。就我个人而言,我永远不会调用'close',而是依赖于RAII--但幸运的是,我可以通过编写不能实现强大IO的代码而逃脱。 –

+0

@KonradRudolph:我猜想依赖于RAII意味着析构函数不会测试关闭是否成功。没有办法将这样的错误条件传输给调用者(除非你使用全局变量)。 –

+0

@Frerich当然。就像我说的那样,我可以放弃使用非强健的IO。懒惰,我知道,但它使代码更简单,更清洁。 –

2

这不是多么ST大量的应该被使用。诚然,这(不幸的是!)编译,甚至做“正确”的事情。但不要写这样的代码。编写这些代码的人可能认为他们很聪明。

但是他们真正做的是通过引入一个新的,非传统的API来打破C++程序员的期望,而没有真正的优势。

此代码从输入流中初始化Person类型的对象。不幸的是,通过这样做,代码放弃了在阅读对象时测试错误的机会。这不好。一个对象应该不是有一个接受输入流的构造函数,它应该重载operator>>

+0

在这里扮演恶魔的主张:我认为将一个流传递给构造函数非常合理。您可以使用异常来指示错误,并避免可能存在未初始化对象的问题(与您建议的替代方法不同,它允许构建对象但不使用'operator >>')。 –

+0

@Frerich原则上,我完全同意你的看法。这也是为什么我说这是一个聪明的程序员,而不是詹姆斯,他认为这是一个对流不懂的人。但它仍然打破了期望,并且可能不是C++构建现有流库顶层的好主意。流可以做得更好,但是然后建立一个自己的库,不要建立在现有的工作模式上,工作方式不同。 –