这种用于方便二进制输出的 C++ fstream 继承的实现如何导致无法打开文件?

How is this implementation of inheritance of C++ fstream for convienient binary output causing failure opening the file?

提问人:reallyhuh 提问时间:8/21/2023 最后编辑:273Kreallyhuh 更新时间:8/21/2023 访问量:59

问:

代码输出,“文件未打开”。

union converter_t {
  char byte[4];
  short int word[2];
  int dword[1];
} converter;

class enhancedfstream: public fstream {
  public:
    enhancedfstream(string s, _Ios_Openmode iosom) {
     fstream(s, iosom);
    }
    void write(int i) {
      converter.dword[0] = i;
      fstream::write(converter.byte, 4);
    }
    void write(short int si) {
      converter.word[0] = si;
      fstream::write(converter.byte, 2);
    }
    void write(string s) {
      char* tca = &s[0];
      fstream::write(tca, s.length());
    }
    void write(char c) {
      // intended correction:
      converter.byte[0] = c;
      fstream::write(converter.byte, 1);
      // as original, before correction:
      // fstream::write(new char[1] {c}, 1);
    }
    void write(char* cp, int i) {
      fstream::write(cp, i);
    }
};

  enhancedfstream fs(fn, fstream::out | fstream::binary | fstream::trunc);
  if (fs.is_open()) {} else cout << "file not open\n";

从 shell 查看 Linux 文件系统显示空文件创建成功,但以编程方式显示文件未打开,后续写入无效。编译和执行是在根目录的子目录中以 root 身份完成的。在 ipadOs iSH 1.3.2(内部版本 494)中使用模拟的 g++ (Alpine 10.3.1_git20210424) 10.3.1 20210424 (C++14)(版权所有 2020)编译。

上面实现的继承是不正确的,还是 fstream 在如何成功地被子类化方面是特殊的?缓冲区设置是否无人值守?标准报告的 fstream 打开失败原因似乎不存在,除非 null 缓冲区导致打开失败。具有相同规格的非子类 fstream 可以正常打开。

C++ 继承二进制 输出 fstream

评论

1赞 HolyBlackCat 8/21/2023
不是答案,但这样使用会泄漏内存。new char[1] {c}
1赞 Retired Ninja 8/21/2023
工会和类型双关语可能值得一读。
1赞 Pepijn Kramer 8/21/2023
在 C++ 中不要使用 use(阅读 C++ 联合行为标准,你不会那么乐意在任何地方使用它 UB)。但即便如此,你正在做的事情也不是真的安全。我也认为没有必要从 fstream 继承,只需制作一些接受流作为第一个输入参数的自由函数(在命名空间中)unionstd::variant
1赞 Pepijn Kramer 8/21/2023
一般来说,二进制序列化比你想象的要难,你甚至确定另一个将要读取你的文件的系统具有与写入你的文件的系统完全相同的内存布局、字节等吗?如果要读取/写入二进制数据,只需使用经过测试的二进制序列化库即可。
0赞 reallyhuh 8/22/2023
@Pepjin Kramer 没有。根本没有计划在任何其他系统上实现此代码。

答:

1赞 Ted Lyngmo 8/21/2023 #1

在构造函数中构造一个临时函数。该临时值在完整表达式的末尾关闭。fstreamfstream

fstream(s, iosom); // opened and closed immediately

此外,是特定于内部实现的类型。不要使用那些。_Ios_Openmode

使用成员初始值设定项列表初始化基类:

enhancedfstream(const std::string& s, std::ios_base::openmode iosom)
    : std::fstream(s, iosom) // now opened correctly
{
    // empty constructor body
}

演示


旁注:s

  • fstream::write(new char[1] {c}, 1);不是一个好主意。它将分配永远不会释放的内存。使用或简单地使用
    write(&c, 1);
    
    put(c);
    
  • 全局不会做任何事情来简化实现。但是,它确实使同时在多个线程中使用 your 变得不安全。converterenhancedfstream
  • 不要对 和 的大小进行硬编码。它们并不总是 4 个字节和 2 个字节。intshort int

评论

0赞 Ted Lyngmo 8/21/2023
@reallyhuh 正如所写的那样,它确实编译得很好。演示
0赞 reallyhuh 8/21/2023
这里的东西乱七八糟。那里提到的主要解决方案就像*实际编写的那样工作。
0赞 Ted Lyngmo 8/21/2023
@reallyhuh太棒了!很高兴它成功了!
0赞 reallyhuh 8/21/2023
int 和 char 大小以及所有这些代码并不意味着除了特定于实现之外,以及数据存储的小端方法。(请不要砍我,或者把我传给那些愿意的人?都不是!
0赞 Ted Lyngmo 8/21/2023
@reallyhuh 如果你问演示网站是否可以信任,那么我会说是的。在分享简短演示时,它是 SO 上人们最常用(如果不是最常用)的之一。“int 和 char 大小以及所有这些代码根本不意味着特定于实现”——好吧,我仍然会用它来表示意图。此外,如果以后要使用函数模板对其进行泛化,则大部分位都已到位。sizeof