【问题标题】:How to overload std::ofstream::put()?如何重载 std::ofstream::put()?
【发布时间】:2019-06-29 08:46:42
【问题描述】:

我想将 int16_t 值写入文件。

因此我尝试重载 std::ofstream::put() 方法。

#include <fstream>
#include <cstdint>

class Ofstream : public std::ofstream
{
public:
    Ofstream( const std::string & s) : std::ofstream(s) {}

    // for little-endian machines
    Ofstream & put(int16_t val)
    {
        char lsb, msb;
        lsb = (char)val;
        val >>= 8;
        msb = (char)val;
        put(lsb) && put(msb);
        return *this;
    }
    ~Ofstream() {}
};
int main()
{
    int16_t val = 0x1234;
    Ofstream ofile( "test");
    ofile.put(val);
}

在这我总是得到一个分段错误,那么有什么问题呢?

【问题讨论】:

  • std::ofstream继承可能是个坏主意。
  • 我认为您的put 版本隐藏了基类版本,因此您会遇到堆栈溢出。使用调试器很容易检查。但是 πάνταῥεῖ 是对的,这不是这样做的方法。
  • 正如约翰所指出的,您的put() 版本隐藏了继承的版本。因此,put() 在您的 put() 中的调用正在递归调用自身。结果是无限递归。从标准流类继承是一个坏主意 - 最好使用包含(即 ostream 作为类的成员,而不是基类)。
  • 我怀疑 OP 期望基类 put(char) 被调用,而不是派生类中的 put(int16_t) 实现,因为它在技术上是更好的匹配(如果它不是被隐藏的话)。
  • @WhozCraig - 是的。太糟糕了,期望正是隐藏规则旨在防止的一个例子。

标签: c++ ofstream


【解决方案1】:

您的put() 函数调用自身而不是基类版本。所以你会得到无限递归,这会导致堆栈溢出。

替换

put(lsb) && put(msb);

std::ofstream::put(lsb) && std::ofstream::put(msb);

【讨论】:

  • 此解决方案错误地假定从 std::ofstream 继承是一个好主意。
  • @Peter,不,它没有。
  • 附带说明:根据我对 OP 问题的理解,他们可能希望在输出文件中包含数字而不是字符。
  • 为什么从 std::i/o/f/s/stream 派生不是一个好主意?除了我自己添加的东西之外,所有方法都将进一步可用。如果需要,我认为标准库的内容是为定制而设计的。
  • @NicoSchumann - 从标准库中的大量类型继承不是一个好主意的原因是它们的设计方式不利于将它们用作基类。关于某些方面的讨论在stackoverflow.com/questions/1073958/… 此外,与隐藏规则发生冲突 - 就像你一样 - 通常表明你的方法有问题(在某些情况下,解决隐藏规则是合适的,但如果那样的话是你的情况,你不需要问这个问题)。
【解决方案2】:

您的代码的主要问题(无限递归调用)已经得到正确回答。

使用明确的作用域

std::ofstream::put(lsb) && std::ofstream::put(msb);

会解决这个问题。

我想将 int16_t 值写入文件。

虽然我的印象是您想以网络字节顺序(大端序)将二进制数写入文件,而不是put characters as text,但这不是您最终想要实现的目标。

以下是我的处理方法 (independently of the current machine architecture):

#include <fstream>
#include <arpa/inet.h>

struct Ofstream {
    std::ofstream os;

    Ofstream( const std::string & s) : os(s,std::ios_base::binary) {}

    void put(uint16_t dt) {
        uint16_t netdt = htons(dt);
        os.write((char*)&netdt,sizeof(netdt))
    }
};

int main() {
    uint16_t val = 0x1234;
    Ofstream ofile("test");
    ofile.put(val);
}

一般来说,从标准库类继承不是一个好主意,除非它们明确打算这样做以实现(即std::ostream)。
而是将它们用作成员变量。

【讨论】:

  • 谢谢,但我不熟悉 arpa 库。另外,如果存在解决方案,我认为留在 std 库中会很好。而且我需要适合 VM 的值,我当时正在编写它(只是为了好玩,作为一个爱好项目)。
  • @NicoSchumann 网络库和htonx() / ntohx() 函数在几乎所有操作系统中都可用(Windows 为winsock.h)。编写与所有主机架构一起使用的网络字节顺序转换并不是一件容易的事。这样做更便于携带。
  • @πάνταῥεῖ,他寻求帮助的问题是导致他的程序崩溃的原因。你在这里解决的是一个不同的 - 较小的 - 问题,与他的要求无关。要求将“最佳答案”标签更改为对所提问题无济于事的答案充其量是不诚实的。
  • @SidS 好吧,这是几十年经验和对问题的更广泛看法的诅咒。请注意,我提到主要问题已在现有答案/ cmets 中得到解决。另请注意,我对您的回答发表了评论 real 问题是什么。请在其他地方投诉。
  • @SidS BTW,除了修复答案中的明显缺陷之外,是什么阻碍了您解决核心问题?我这边没有不诚实,我看不到。
猜你喜欢
  • 2018-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-19
  • 2010-10-04
  • 2020-11-05
  • 2010-09-26
  • 1970-01-01
相关资源
最近更新 更多