如何从std :: ostream继承?

Mic*_*ael 35 inheritance stl ostream

我一直在谷歌搜索,我只是找不到一个简单的答案.它应该很简单,就像STL一般.

我想定义从std :: ostream公开继承的MyOStream.假设我想在每次将某些内容写入流中时调用foo().

class MyOStream : public ostream {
public:
  ...
private:
   void foo() { ... }
}
Run Code Online (Sandbox Code Playgroud)

我知道ostream的公共接口是非虚拟的,那怎么办呢?我希望客户能够在MyOStream上使用operator <<和write()以及put()并使用我的类的扩展功能.

Ben*_*Ben 30

我正在围绕如何做同样的事情,我发现它实际上并不那么难.基本上只是对ostream和streambuf对象进行子类化,并将自身构造为缓冲区.将为发送到流的每个字符调用std :: streambuf中的虚拟overflow().为了适应你的例子我刚刚创建了一个foo()函数并调用它.

struct Bar : std::ostream, std::streambuf
{
    Bar() : std::ostream(this) {}

    int overflow(int c)
    {
        foo(c);
        return 0;
    }


    void foo(char c)
    {
        std::cout.put(c);

    }
};

void main()
{
    Bar b;
    b<<"Look a number: "<<std::hex<<29<<std::endl;
}
Run Code Online (Sandbox Code Playgroud)

哦,忽略主要功能不是真正的主要功能的事实.它位于从其他地方调用的命名空间中; p

  • 对放入流中的每个字符调用 put() 非常慢。 (3认同)
  • 完美的工作!应该是接受的答案,尽管这是一个老问题. (2认同)

小智 24

不幸的是,这不是一个简单的问题.您应该派生的basic_类是类,例如basic_ostream.但是,从流中派生可能不是您想要的,您可能希望从流缓冲区派生,然后使用此类来实例化现有流类.

整个区域很复杂,但是有一本关于标准C++ IOStreams和Locales的优秀书籍,我建议你在进一步研究之前先看一下.


Ela*_*ich 22

另一个实现类似效果的工作黑客是使用模板和组合

class LoggedStream {
public:
  LoggedStream(ostream& _out):out(_out){}
  template<typename T>
  const LoggedStream& operator<<(const T& v) const {log();out << v;return *this;}
protected:
  virtual void log() = 0;
  ostream& out;
};

class Logger : LoggedStream {
  void log() { std::cerr << "Printing" << std::endl;}
};

int main(int,char**) {LoggedStream(std::cout) << "log" << "Three" << "times";}
Run Code Online (Sandbox Code Playgroud)

  • @ElazarLeibovich:它适用于`std :: hex`但不适用于`std :: endl`或`std :: flush`(可能还有其他一些).这是因为它无法将T解析为适当的函数类型.将以下内容添加到`LoggedStream`可以解决问题:`LoggedStream const&operator <<(std :: ostream&(*F)(std :: ostream&))const {F(out); 返回*这个; }` (8认同)

Hen*_*ino 5

我不知道这是否是正确的解决方案,但我从这种方式继承了std :: ostream.它使用从std :: basic_streambuf继承的缓冲区,一次获取64个字符(如果刷新则更少),并将它们发送到通用的putChars()方法,在该方法中完成数据的实际处理.它还演示了如何提供用户数据.

实例

#include <streambuf>
#include <ostream>
#include <iostream>

//#define DEBUG

class MyData
{
    //example data class, not used
};

class MyBuffer : public std::basic_streambuf<char, std::char_traits<char> >
{

public:

    inline MyBuffer(MyData data) :
    data(data)
    {
        setp(buf, buf + BUF_SIZE);
    }

protected:

    // This is called when buffer becomes full. If
    // buffer is not used, then this is called every
    // time when characters are put to stream.
    inline virtual int overflow(int c = Traits::eof())
    {
#ifdef DEBUG
        std::cout << "(over)";
#endif
        // Handle output
        putChars(pbase(), pptr());
        if (c != Traits::eof()) {
            char c2 = c;
            // Handle the one character that didn't fit to buffer
            putChars(&c2, &c2 + 1);
        }
        // This tells that buffer is empty again
        setp(buf, buf + BUF_SIZE);

        return c;
    }

    // This function is called when stream is flushed,
    // for example when std::endl is put to stream.
    inline virtual int sync(void)
    {
        // Handle output
        putChars(pbase(), pptr());
        // This tells that buffer is empty again
        setp(buf, buf + BUF_SIZE);
        return 0;
    }

private:

    // For EOF detection
    typedef std::char_traits<char> Traits;

    // Work in buffer mode. It is also possible to work without buffer.
    static const size_t BUF_SIZE = 64;
    char buf[BUF_SIZE];

    // This is the example userdata
    MyData data;

    // In this function, the characters are parsed.
    inline void putChars(const char* begin, const char* end){
#ifdef DEBUG
        std::cout << "(putChars(" << static_cast<const void*>(begin) <<
            "," << static_cast<const void*>(end) << "))";
#endif
        //just print to stdout for now
        for (const char* c = begin; c < end; c++){
            std::cout << *c;
        }
    }

};

class MyOStream : public std::basic_ostream< char, std::char_traits< char > >
{

public:

    inline MyOStream(MyData data) :
    std::basic_ostream< char, std::char_traits< char > >(&buf),
    buf(data)
    {
    }

private:

    MyBuffer buf;

};

int main(void)
{
    MyData data;
    MyOStream o(data);

    for (int i = 0; i < 8; i++)
        o << "hello world! ";

    o << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)