我一直在谷歌上搜索,我找不到一个简单的答案。它应该很简单,因为STL通常是。
我想定义从std :: ostream公开继承的MyOStream。假设我想在每次将某些内容写入流中时调用foo()。
class MyOStream : public ostream {
public:
...
private:
void foo() { ... }
}
据我所知,ostream的公共接口是非虚拟的,那怎么办呢? 我希望客户能够同时使用运营商<<并且在MyOStream上使用write()和put()并使用我班级的扩展能力。
答案 0 :(得分:26)
我正在围绕如何做同样的事情,我发现它实际上并不那么难。 基本上只是对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;
}
哦,忽略了主要功能不是真正的主要功能的事实。它位于从其他地方调用的命名空间中; p
答案 1 :(得分:24)
不幸的是,这不是一个简单的问题。您应该派生的类是basic_
类,例如basic_ostream
。但是,从流中派生可能不是您想要的,您可能希望从流缓冲区派生,然后使用此类来实例化现有流类。
整个区域很复杂,但有一本关于它的优秀书籍Standard C++ IOStreams and Locales,我建议你在进一步研究之前先看一下。
答案 2 :(得分:21)
实现类似效果的另一个工作黑客是使用模板和组合
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";}
答案 3 :(得分: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;
}
答案 4 :(得分:0)
组成,而不是继承。你的类包含,“包装”一个ostream&amp;并转发给它(在调用foo()之后)。