【问题标题】:C++, reading a file using ifstreamC++,使用 ifstream 读取文件
【发布时间】:2012-05-21 06:56:51
【问题描述】:
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;
}

请你解释一下,下面的代码 sn-p 是如何工作的? if (fin == NULL) { 休息; }

fin 是堆栈上的对象,而不是指针,因此它不能变为 NULL。 我无法在 ifstream 类中找到重载的 operator== 函数。 所以我无法理解这个 sn-p 是如何工作的。

【问题讨论】:

    标签: c++ ifstream


    【解决方案1】:

    ifstream 类有一个operator void *() (or operator bool() in C++11)。这就是你测试(fin == NULL)时所调用的。

    测试fin == NULL 应该与测试fin.fail() 完全相同。

    【讨论】:

    • 非常感谢。你的回答很有帮助。
    【解决方案2】:

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

    从未打算将此转换的结果用作 指针和fin == NULL 之类的代码显示出极差 了解 C++ 和标准流。惯用的方式 编写第一个循环是定义一个默认构造函数和一个 operator&gt;&gt;Person,然后写:

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

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

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

    .)

    【讨论】:

    • 虽然我同意这样做可能是最佳实践,但我从未见过测试关闭是否成功并调整返回值的代码。就我个人而言,无论如何我从不打电话给close,而是依靠 RAII——但幸运的是,我可以编写不做健壮 IO 的代码。
    • @KonradRudolph:我猜依赖 RAII 意味着析构函数也不会测试关闭是否成功。没有办法将这样的错误情况传输给调用者(除非您求助于全局变量)。
    • @Frerich 当然。就像我说的,我可以摆脱非鲁棒 IO。懒惰,我知道,但它使代码更简单、更干净。
    • @KonradRudolph 我从未编写过既不刷新并检查(如果输出为std::cout)或关闭并检查的代码。我认为不这样做是一个严重的错误。使用程序的常见习惯用法类似于:prog filename &gt; tmp &amp;&amp; mv tmp filename。如果您在尚未真正成功写入数据的情况下返回成功状态,用户将不会高兴。
    • @James 我同意——如果曾经观察到这个问题。事实上,我从来没有(曾经)在 IO 方面遇到过任何问题,当然,除了使用可移动设备(并在读取过程中将其移除)或类似的恶作剧时。当然,一旦您开始处理大文件、有磁盘配额或......但是对于我的简单需求来说已经足够了。但我当然不会建议这种神风敢死队的编程风格。
    【解决方案3】:

    这不是应该使用流的方式。没错,这(不幸的是!)编译甚至做了“正确”的事情。但是不要写这样的代码。编写此代码的人可能认为他们很聪明。

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

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

    【讨论】:

    • 在这里扮演魔鬼倡导者:我认为将流传递给构造函数非常有意义。您可以使用异常来表示错误,并避免可能出现未初始化对象的问题(与您建议的替代方案不同,它允许构造对象但不使用operator&gt;&gt;)。
    • @Frerich 原则上,我完全同意你的看法。这也是为什么我说这是一个聪明的程序员,而不是詹姆斯认为这是一个对流一无所知的人。但它仍然超出预期,并且在基于现有流库的 C++ 构建中可能不是一个好主意。 Streams 可以做得更好,但是构建一个自己的库,不要在现有的模式之上构建,它的工作方式不同。
    猜你喜欢
    • 2010-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-13
    • 1970-01-01
    相关资源
    最近更新 更多