为什么不从ifstream继承

时间:2014-04-24 13:20:49

标签: c++ stream ifstream streambuf

我想创建一个自动删除注释和其他垃圾数据的自定义输入文件流。我提出了以下解决方案:

class FileReader : public std::ifstream
{
public:
  explicit FileReader(const char* fName) { open(fName); }

  ~FileReader() { if (is_open()) close(); }

  template <typename T, bool IsBaseOfSerializable>
  struct DoRead
  {
    void operator()(std::ifstream& ifs, T& data) { ifs >> data; }
  };

  template <typename T>
  struct DoRead<T, true>
  {
    void operator()(FileReader& reader, T& data) { data.Deserialize(reader); }
  };

  template <typename T>
  friend FileReader& operator>>(FileReader& reader, T& data)
  {
    reader.SkipCommentsAndGarbage();
    DoRead<T, std::is_base_of<ISerializable, T>::value> doread;
    doread(reader, data);
    return reader;
  }

  void SkipCommentsAndGarbage() {... }
};

我还有包含ISerializable方法的接口Serialize/Deserialize。一切看起来都很好。

但我已经读过,我不应该继承std::ifstream并且应该创建自定义std::streambuf

您能否解释为什么从std::ifstream继承是不好的?如何创建以类似方式忽略评论和其他数据的自定义std::streambuf

3 个答案:

答案 0 :(得分:4)

我不清楚你希望你的班级如何工作。该 operator>>中的std::istream个函数虚拟,并且 迟早(通常很快,在编写良好的代码中),你好吗? 以std::istream&结束。您有转发的想法 在某些情况下使用,但在这种情况下,你不能继承 std::ifstream;你包含一个指向istream的指针,和 转发它。 (通过不继承,你确保你不能继承 以istream&结束。这是限制性的,但可以接受 某些情况。)

这样做的正常方法是提供过滤 streambuf,用于过滤输入文本。例如,如果 评论从#到最后一行,你不会 不得不担心引用等等,简单的事情 以下工作:

class UncommentStreambuf : public std::streambuf
{
    std::streambuf* mySource;
    std::istream*   myOwner;
    char            myBuffer;
protected:
    int underflow() override
    {
        int results = mySource->sbumpc();
        if ( results == '#' ) {
            while ( mySource->sgetc() != '\n' ) {
                mySource->sbumpc();
            }
        }
        if (results != traits_type::eof()) {
            myBuffer = results;
            setg( &myBuffer, &myBuffer, &myBuffer + 1 );
        } else {
            setg( nullptr, nullptr, nullptr );
        }
        return results;
    }
public:
    UncommentStreambuf( std::streambuf* source )
        : mySource( source )
        , myOwner( nullptr )
    {
    }
    UncommentStreambuf( std::istream& source )
        : mySource( source.rdbuf() )
        , myOwner( &source )
    {
        source.rdbuf( this );
    }
    ~UncommentStreambuf()
    {
        if ( myOwner != nullptr ) {
            myOwner->rdbuf( mySource );
        }
    }
};

如果您需要处理其他类型的评论,或担心 引用评论字符,你需要更多的逻辑(可能 用于收集字符的私有缓冲区,以便进行测试 一个多个字符的序列)。

答案 1 :(得分:2)

因为公开继承以修改ifstream的行为会导致意想不到的后果,因为它仍然是istream并且在许多情况下会被视为这样 - 剥离您的自定义行为:

  • FileReader传递给期望::std::istream&的函数时,将不会使用您的自定义功能

  • 任何具有C等运营商的班级template<typename T> T& operator>>(T& in, C& target);都将无法使用

  • 只要直接使用基础istream的任何功能,您的包装函数就不起作用

在您的情况下,您可以简单地切换为将ifstream作为成员或将其继承更改为私有。

答案 2 :(得分:1)

它基本上是std::iostream设计来自定义std::streambuf,这是所有特定于上下文的功能所在的位置。