• 小一日一
    2019-12-11
    看了老师的代码再看自己学的代码,感觉我的C++是小学生水平。

    以为自己看过几遍C++ PRIMER 5th, 看过并理解effective++, more effective c++, inside the c++ object model, 能应付平时的开发需要,也能看懂公司别人的代码,就觉得自己的C++不错了,看了老师github的代码后我是彻底服了,感叹C++太博大精深,永远不敢说自己精通C++。

    我什么时候才能达到老师对C++理解并使用的高度呢,难道也需要20年么?
    展开

    作者回复: 肯定还有更好的 C++ 代码的。学习无止境!

    认真学习,应该不用那么久(我还没有极客时间专栏来帮助我学习呢😌)。

    反过来,说明老程序员还有点价值么。🤗

    
     4
  • 晚风·和煦
    2019-12-11
    从 C++17 开始,I 和 S 可以是不同的类型。这带来了更大的灵活性和更多的优化可能性。 没太理解这句话😂

    作者回复: 现在 r.begin() 和 r.end() 可以是不同类型了。

    
     2
  • Geek_b68b74
    2020-01-17
    1、使用输入行迭代器 这一部分里,“ auto&& r = istream_line_reader(is);” 这里为什么要用右值引用呢?
    2、还是使用输入行迭代器这里,

    for (const string& line :
         istream_line_reader(is)) {
      // 示例循环体中仅进行简单输出
      cout << line << endl;
    }
    “获取冒号后边的范围表达式的结果,并隐式产生一个引用,在整个循环期间都有效。注意根据生命期延长规则,表达式结果如果是临时对象的话,这个对象要在循环结束后才被销毁。”
     第一句是说line在整个循环期间有效?这是想表达什么呢?还有第二句,指的是哪个临时对象呢?在哪个循环结束后销毁呢?期待您的解答
    展开

    作者回复: 你的两个疑问实际是针对同一个地方。

    auto&& 那句是用一个“万能”引用捕获一个对象,左值和右值都可以。C++ 的生命期延长规则,保证了引用有效期间,istream_line_reader 这个“临时”对象一直存在。没有生命期延长的话,临时对象在当前语句执行结束后即销毁。

    
     1
  • robonix
    2020-01-02
    老师,iterator begin()函数返回一个iterator对象,这个对象还包含了string成员,这样就得拷贝了吧,效率会不会不高呢

    作者回复: 好问题。不过,你和getline的版本仔细分析比较一下的话,也是一个string,大家半斤八两。毕竟,按我们的用法,一个流上你也只会用一个有效的istream_line_reader::iterator。另外,遍历的时候我用的是const string&,没有额外的拷贝。

    
     1
  • Encoded Star
    2019-12-24
    课后思考
    1.目前这个输入迭代器的行为,在干什么情况下可能导致意料之外的后果?
    答:目前这个输入迭代器在构造里调用了++,所以,多一次构造就可能读到意料之外的结果了。

    2.请尝试一下改进这个输入行迭代器,看看能不能消除这种意外,如果可以,该怎么做?如果不可以,为什么?
    答:可以啊,文章里提到了,这个输入行迭代器构造的使用了++,是为了与日常使用一致,如果想改进这个一块,我们也可以改构造的时候
    展开

    作者回复: 1 对。2 你需要自己实验一下,再想想会不会有其他副作用。

    
     1
  • 总统老唐
    2019-12-16
    吴老师,这一课有两个疑问:
    1,“到底应该让 * 负责读取还是 ++ 负责读取”,该怎样理解?如果“读取”指的是在istream上读取一行,放入line_成员中,用++实现这个操作是最常见和直觉的,同时,用 * 返回读取的内容也在最容易想到的方式,反过来,什么情况下会需要”用*来负责读取“?
    2,输入迭代器为什么要定义 iterator operator++(int)

    作者回复: 1. 用 ++ 是最合理的,但也有一个奇怪的地方,目前还没人说到。

    2. 这个就是后置 ++。迭代器要求前置和后置 ++ 都要定义,虽然我目前只使用了前置版本。

    
     1
  • 旭东
    2019-12-14
    老师,您好,iterater中后置++的实现是不是应该返回const;避免(i++)++这样的代码通过编译?

    作者回复: 1. 不能写 const,因为你修改了自己。

    2. 就算能写也防不了,因为你返回的是个全新的对象。

    
     1
  • 我叫bug谁找我
    2019-12-13
    遍历一遍后,第二次调用begin会崩溃,stream_指针已经为空

    作者回复: 作为input iterator,本来你就不应该遍历第二次的。这个不是问题。

    
     1
  • nelson
    2019-12-12
    如果stream_是nullptr会怎么样?

    作者回复: 得到一个空的不能遍历的迭代器。跟任何 end() 相等比较返回真,因而你不可以对它做 ++ 操作。如果你要硬来,它就死给你看。

     1
     1
  • 禾桃
    2019-12-11
    输入迭代器和输出迭代器,
    这个入和出是相对于什么而言的?
    感觉有点绕。

    谢谢!

    作者回复: cout << *it 就是读;
    *it = 42 就是写。

    
     1
  • Geek_71d4ac
    2019-12-11
    在构造函数中使用this是否安全?万一构造中途失败了呢?

    作者回复: 本身没有任何问题。如何保证行为安全(如异常安全)是个独立问题,跟是否在构造函数里没啥关系。尽量不使用裸指针非常重要,用了的话就需要照顾很多细节了……

    
     1
  • Jason
    2020-01-03
    针对第二个问题,我回头想这是一个输入迭代器,不需要满足多次访问,所以两次begin不是它的职责范围吧,使用新标准的for循环就不会出现意外结果。老师是这样吗?想了一晚上,哈哈

    作者回复: 从实际使用上讲是这样。单从规定上来讲,输入迭代器不禁止调用begin多次的。所以目前的行为不理想,但一般没问题。

    
    
  • 千鲤湖
    2019-12-18
    过来看看老师问的那两个问题,好奇中。。。

    作者回复: 公布第 1 个问题的答案吧:

    #include <fstream>
    #include <iostream>
    #include "istream_line_reader.h"

    using namespace std;

    int main()
    {
        ifstream ifs{"test.cpp"};
        istream_line_reader reader{ifs};
        auto begin = reader.begin();
        for (auto it = reader.begin();
             it != reader.end(); ++it) {
            cout << *it << '\n';
        }
    }

    以上代码,因为 begin 多调用了一次,输出就少了一行……

    
    
  • 千鲤湖
    2019-12-16
    1.可能是operator==中,比较时没有获取当前文件流位置,这样的话,无法比较不同istream(同一个文件)创建的iterator? 

    2 采用ftell获取当前文件流位置

    作者回复: 不是我想的那个……

    这个是个问题,但一般不必解决。要能够比较,对性能影响太大。我线上的版本里是有下面这段注释的:

    // This implementation basically says, any iterators
    // pointing to the same stream are equal. This behaviour
    // may seem a little surprising in the beginning, but, in
    // reality, it hardly has any consequences, as people
    // usually compare an input iterator only to the sentinel
    // object. The alternative, using _M_stream->tellg() to
    // get the exact position, harms the performance too dearly.
    // I do not really have a better choice.
    //
    // If you do need to compare valid iterators, consider using
    // file_line_reader or mmap_line_reader.

    
    
  • 禾桃
    2019-12-15
    #1 目前这个输入行迭代器的行为,在什么情况下可能导致意料之外的后果?
        auto x = istream_line_reader();
        auto xit = x.begin();
       这个函数会调用istream_line_reader:: iterator::operator++() {
       getline(*nullptr, _M_line); <---- 死翘翘 }
    但是用户觉得,我只是调用了x.begin, 不至于死的这么突然吧:(

    #2 请尝试一下改进这个输入行迭代器,看看能不能消除这种意外。如果可以,该怎么做?如果不可以,为什么?
    看了您的git代码, 看到了对nullptr的识别和抛出异常的处理,这是个解决方案。或者我们可以istream_line_reader() = delete? 没想到我们需要构造函数istream_line_reader() 的场景。
    展开

    作者回复: #1 对我来讲,这不是意外。就像你对空指针解引用崩溃也不是意外一样。没有有效的 istream,你要取这个流的开头,出错很正常。

    #2 因为你没有看到我想的问题,所以第二部分也不是我要的回答……

    
    
  • MT
    2019-12-14
    老师,这次是上次关于那个例子的补充:
    1. 在进行迭代的时候,begin()和end()方法,即你所说的,编译器会自动生成指向数组头尾的指针
    2. 在end()方法内返回了struct null_sentinel{} 的一个对象,即 I 和 S 的类型不同
    3.通过使用 struct null_sentinel{};所提供的operator!=() 从而达到对字符串遍历的截至
    如有不对,请老师指出
    之后我尝试过,在 c_string_view{} 中的 end 方法,返回一个它本身的对象,并为NULL,同时重载它的 != 运算符,但是失败了。
    我想问下,这便算是属于一种更多的优化可能性吗?在之后,若需要只需要修改struct null_sentinel{};即可?
    展开

    作者回复: 前面部分没有问题。后面的失败部分,没看懂你的意思。

    我这个例子重点在于,null_sentinel 表示的不是一个位置,而是一个条件。我们可以用迭代器来表示一个条件,这是对它的功能的很大扩展。虽然这种扩展方式性能非常好,但这个功能主要不是优化,而是新的可能性。

    
    
  • 木瓜777
    2019-12-13
    iterator operator++(int) { iterator temp(*this); ++*this; return temp; }

    这个拷贝构造,是否会出问题? 如果失败,this继续读取下一行,但temp是异常的。

    作者回复: 拷贝构造失败的话,直接抛异常了,当然不会继续读取下一行。

    
    
  • MT
    2019-12-12
    老师,可以讲以下为什么可以将I 和 S 设置成不同的类型吗?具体使用在那些方面?

    作者回复: 给个例子你仔细研读一下吧。功能是遍历字符串,直到遇到字符串结尾(事先不知道字符串长度)。

    #include <stdio.h>

    struct null_sentinel {};

    bool operator!=(const char* ptr, null_sentinel)
    {
        return *ptr != 0;
    }

    // operator!=(null_sentinel, const char* ptr), operator==, ...

    struct c_string_view {
        c_string_view(const char* str) : str_(str) {}
        const char* begin() const { return str_; }
        null_sentinel end() const { return null_sentinel{}; }
        const char* str_;
    };

    int main()
    {
        c_string_view msg{"Hello world!"};
        for (char ch : msg) {
            putchar(ch);
        }
        putchar('\n');
    }

    
    
  • Scott
    2019-12-12
    我的理解是istream_line_reader的iterator在到达end时,再++会直接crash,这个和STL里面主流容器的行为是不一致的。
    可以在get_line之前,判断一下stream_是否为nullptr,不是才调用,对end的iterator反复进行++都一直返回自己本身。

    作者回复: 你对容器的 end() 解引用,同样可能崩溃(取决于实现)。你不被允许这么做。这么做,你就进入了 undefined behavior 的领域,系统是死还是出 bug 都正常。

    
    
  • tt
    2019-12-11
    意料之外的后果,是不是主要就是资源发生了不可控或不可知的泄露或状态改变?

    这里的资源我觉得一是string对象,一个是istream对象,那么在这两个对象的内存管理上会引起问题?

    比如构造函数中传入的istream指针没有被管理起来,它指向的对象如果被析构就会发生异常?

    作者回复: 你说的情况会出问题,但这个是需要调用者保证的,我做不了什么事情。

    再想想。🤓

    
    
我们在线,来聊聊吧