كيف ترث من std::ostream؟
-
12-09-2019 - |
سؤال
لقد كنت أبحث في Google ولم أجد إجابة بسيطة لهذا السؤال.ويجب أن تكون بسيطة، كما هي الحال في المحكمة الخاصة بلبنان بشكل عام.
أريد تحديد MyOStream الذي يرث علنًا من std::ostream.لنفترض أنني أريد الاتصال بـ foo() في كل مرة يتم فيها كتابة شيء ما في الدفق الخاص بي.
class MyOStream : public ostream {
public:
...
private:
void foo() { ... }
}
أدرك أن الواجهة العامة لـ ostream غير افتراضية، فكيف يمكن القيام بذلك؟أريد أن يكون العملاء قادرين على استخدام كل من عامل التشغيل << والكتابة () ووضع () على MyOStream واستخدام القدرة الموسعة لفصلي.
المحلول
إنه ليس سؤالًا بسيطًا، للأسف.الفئات التي يجب أن تستمد منها هي basic_
الطبقات، مثل basic_ostream
.ومع ذلك، قد لا يكون الاشتقاق من الدفق هو ما تريده، وقد ترغب في الاشتقاق من مخزن مؤقت للدفق بدلاً من ذلك، ثم استخدام هذه الفئة لإنشاء مثيل لفئة دفق موجودة.
المنطقة بأكملها معقدة، ولكن هناك كتاب ممتاز عنها معيار C++ IOStreams واللغات, ، والتي أقترح عليك إلقاء نظرة عليها قبل المضي قدمًا.
نصائح أخرى
كنت أدور برأسي حول كيفية القيام بنفس الشيء واكتشفت أن الأمر في الواقع ليس بهذه الصعوبة.في الأساس، ما عليك سوى إنشاء فئة فرعية من كائنات ostream وstreambuf، وإنشاء ostream مع نفسه كمخزن مؤقت.سيتم استدعاء التجاوز الظاهري () من std::streambuf لكل حرف يتم إرساله إلى الدفق.لملاءمة المثال الخاص بك، قمت للتو بإنشاء وظيفة 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
هناك حيلة عمل أخرى لتحقيق تأثير مماثل وهي استخدام القالب والتركيب
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";}
لا أعرف إذا كان هذا هو الحل الصحيح، ولكنني ورثت من 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;
}
التكوين وليس الميراث.يحتوي فصلك على "التفاف" على ostream&، وإعادة توجيهه إليه (بعد استدعاء foo()).