C++继承std::fstream时set_rdbuf()使用异常,正确用法是什么?
你对set_rdbuf的使用方法确实存在错误,核心问题是调用时机不符合该接口的设计要求,结合MSVC的STL实现特性,具体原因如下:
核心原因
std::fstream的默认构造函数会自动创建一个内置的std::filebuf实例,受保护成员函数set_rdbuf的设计作用是在流完成初始化之前替换内置缓冲区,在流初始化完成(默认构造执行完毕)后调用set_rdbuf属于实现定义行为,MSVC的STL实现中此时调用该接口不会正确完成缓冲区替换,甚至可能修改传入缓冲区的内部状态,这就是你第一个示例中rdbuf()->is_open()返回0的根本原因。
另外std::fstream的is_open()成员函数本质是检查自身关联的filebuf的打开状态,你没有通过流本身的open()方法打开文件,流内部的状态标记也不会同步更新。
为什么swap方案正常工作
你调用std::fstream::rdbuf()->swap(*m_file_buf)时,并没有替换流关联的缓冲区,只是将你自定义filebuf的所有状态(包括打开的文件句柄、读写位置等)和流内置的filebuf做了交换。流本身始终在操作内置的filebuf,交换后内置filebuf变为打开状态,所以所有输出都符合预期。
正确使用set_rdbuf的方式
如果需要让std::fstream的所有原生方法(open/close/is_open等)都操作你自定义的filebuf,需要在构造函数的初始化阶段就调用set_rdbuf完成替换,再执行打开操作,示例代码如下:
class A : public std::fstream { private: std::filebuf m_file_buf; public: A() : std::fstream() { // 先替换缓冲区,再调用流的open方法 set_rdbuf(&m_file_buf); open("test_file.txt", std::ios_base::out | std::ios_base::in); if (!is_open()) { std::cout << "something went wrong"; } std::cout << "m_ file buff is: " << m_file_buf.is_open() << "\n"; std::cout << "rd file buff is: " << rdbuf()->is_open() << "\n"; std::cout << "we are: " << is_open() << "\n"; } };
上述代码运行后三个输出均为1,符合预期。
替代方案:使用公开接口替换缓冲区
如果你不需要复用std::fstream的open/close方法,完全不需要继承std::fstream,直接调用std::basic_ios公开的rdbuf(std::streambuf*)方法即可替换缓冲区:
int main() { std::filebuf custom_buf; if (!custom_buf.open("test_file.txt", std::ios_base::out | std::ios_base::in)) { std::cout << "something went wrong"; } std::fstream fs; // 直接替换缓冲区,不需要继承 fs.rdbuf(&custom_buf); // 此时可以正常用fs读写,注意不要调用fs.open/fs.close,直接操作custom_buf即可 return 0; }
内容的提问来源于stack exchange,提问作者Tomas Berger

