我想创建一个自动删除注释和其他垃圾数据的自定义输入文件流。我提出了以下解决方案:
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
?
答案 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
,这是所有特定于上下文的功能所在的位置。