C++,使用ifstream读取文件

7
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; }
fin是一个栈上的对象,不是指针,因此它不能成为NULL。 我无法在ifstream类中找到重载的operator==函数。 所以我无法理解这个代码段是如何工作的。
3个回答

5

非常感谢您。 您的回答非常有帮助。 - Yoh0xFF

3
istreamostream的基类具有隐式转换函数,使它们可以用作布尔值;在C++11之前,隐式转换是指向void*的。

这种转换的目的从未是将其结果用作指针,像fin == NULL这样的代码表明了对C++和标准流的极差理解。编写第一个循环的惯用方式是定义一个默认构造函数和Personoperator>>,然后编写:

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

顺便提一下:您应该测试fin.close()的返回值,如果失败了就不要返回0

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

.)


虽然我同意这可能是最佳实践,但我从未见过测试关闭是否成功并调整返回值的代码。就我个人而言,我从不调用 close,而是依赖于 RAII;但幸运的是,我可以写一些不需要做强大 IO 的代码。 - Konrad Rudolph
@KonradRudolph:我猜依赖RAII意味着析构函数也不会测试关闭是否成功。没有办法将这种错误条件传递给调用者(除非您诉诸全局变量)。 - Frerich Raabe
@Frerich 当然可以。就像我说的,我可以使用非健壮的IO。虽然有点懒,但这使得代码更简单、更清晰。 - Konrad Rudolph
@KonradRudolph 我从未编写过不刷新和检查代码(如果输出是 std::cout)或不关闭和检查代码的情况。我认为不这样做是一个严重的错误。使用程序的常见习惯用法是类似于:prog filename > tmp && mv tmp filename。如果你在实际上没有成功写入数据时返回成功状态,用户将不会感到满意。 - James Kanze
@James 我同意 - 如果曾经观察到这个问题。但事实上,我从来没有(从来没有)遇到过除了使用可移动设备(并在读取中拔出)或类似的恶作剧之外的任何IO问题。当然,一旦开始处理大文件、有磁盘配额或者……就会出现问题。但对于我的简单需求来说,这已经足够了。但我肯定不会建议这种神风式的编程方式。 - Konrad Rudolph
@KonradRudolph 磁盘已满是写入失败的常见原因。如果您为自己编写快速程序,并且驱动器上有足够的空间,则可以正常运行。否则...我还没有在公司工作过,这里偶尔不会出现磁盘已满的情况。 - James Kanze

2

流不应该被这样使用。确实,这段代码(不幸的是!)可以编译并且甚至执行了“正确”的操作。但是不要写这样的代码。写这段代码的人可能认为他们很聪明。

但是他们真正做的是打破C++程序员的期望,引入一个新的、不传统的API,并没有真正的优势。

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


在这里打个反对的小报告:我认为将流传递给构造函数是非常合理的。你可以使用异常来表示错误,并且避免了可能未初始化对象的问题(与您建议的替代方案不同,它允许构造一个对象但不能使用 operator>>)。 - Frerich Raabe
@Frerich 原则上,我完全同意你的观点。这也是我说这是一个聪明的程序员而不是詹姆斯认为这是一个对流一无所知的人的原因。但它仍然打破了期望,可能在 C++ 上建立在现有流库之上并不是一个好主意。流可以做得更好,但是要构建自己的库,而不是构建在工作方式不同的现有模式之上。 - Konrad Rudolph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接