2009-04-21 29 views
25

Tôi đã googling xung quanh và tôi chỉ không thể tìm thấy một câu trả lời đơn giản này. Và nó phải đơn giản, như STL nói chung là.Làm thế nào để kế thừa từ std :: ostream?

Tôi muốn xác định MyOStream kế thừa công khai từ std :: ostream. Giả sử tôi muốn gọi foo() mỗi khi có nội dung nào đó được ghi vào luồng của tôi.

class MyOStream : public ostream { 
public: 
    ... 
private: 
    void foo() { ... } 
} 

Tôi hiểu rằng giao diện công khai của luồng không phải là ảo, vậy làm thế nào nó có thể được thực hiện? Tôi muốn khách hàng có thể sử dụng cả hai nhà điều hành < < và viết() và đặt() trên MyOStream và sử dụng khả năng mở rộng của lớp học của tôi.

+3

STL có thể đơn giản, nhưng đó chỉ là một phần của thư viện chuẩn C++. Thư viện iostreams không liên quan gì đến STL. STL về cơ bản là contianers + iterators + algorithm. Iostreams, miền địa phương và tất cả những gì có nguồn gốc hoàn toàn khác nhau, và thường là một nỗi đau để làm việc với;) – jalf

Trả lời

19

Thật không phải là một câu hỏi đơn giản, thật không may. Các lớp học bạn nên lấy được là các lớp học basic_, chẳng hạn như basic_ostream. Tuy nhiên, dẫn xuất từ ​​một luồng có thể không phải là những gì bạn muốn, bạn có thể muốn lấy ra từ một bộ đệm luồng thay vào đó, và sau đó sử dụng lớp này để khởi tạo một lớp luồng hiện có.

Toàn bộ khu vực là phức tạp, nhưng có một cuốn sách tuyệt vời về nó Standard C++ IOStreams and Locales, mà tôi đề nghị bạn hãy xem trước khi đi thêm nữa.

+0

Tôi sẽ tìm kiếm bản sao của cuốn sách này, nhưng bạn đã lưu tôi phải làm. +1 – Evan

+0

@anon Tôi không thể tìm thấy bất kỳ tham chiếu nào đến luồng trên web. Nó là gì? –

0

Thành phần, không phải thừa kế. Lớp học của bạn chứa "kết thúc tốt đẹp" một ostream & và chuyển tiếp đến nó (sau khi gọi foo()).

+0

Vui lòng đăng một số mã minh họa cách điều này sẽ làm việc với các toán tử << hiện có. Và chú ý foo() sẽ được gọi mỗi khi một toán tử được sử dụng. –

+6

Thành phần không phải luôn luôn là giải pháp tốt nhất, cũng giống như thừa kế không phải là. ostream có một tá các toán tử quá tải được thực hiện cho nó, bạn không mong đợi bất cứ ai thực sự viết lại tất cả các giao diện công cộng của ostream chỉ để thêm một chức năng nhỏ vào một lớp. – Michael

4

Tôi không biết đây có phải là giải pháp đúng hay không, nhưng tôi được thừa hưởng từ std :: ostream theo cách này. Nó sử dụng một bộ đệm được thừa kế từ std :: basic_streambuf và nhận được 64 ký tự cùng một lúc (hoặc ít hơn nếu được flushed) và gửi chúng đến một phương thức putChars() chung nơi xử lý dữ liệu thực tế được thực hiện. Nó cũng thể hiện cách cung cấp dữ liệu người dùng.

Live Example

#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; 
} 
19

làm việc khác hack để đạt được một hiệu ứng tương tự là sử dụng mẫu và thành phần

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";} 
+0

điều này không hỗ trợ các công cụ như 'hex' hoặc' endl' – shoosh

+0

Tại sao không? 'T' sẽ lấy kiểu' std :: hex'. –

+6

@ElazarLeibovich: Nó hoạt động cho 'std :: hex' nhưng không cho' std :: endl' hoặc 'std :: flush' (và có lẽ một số khác). Điều này là bởi vì nó không thể giải quyết T đến loại hàm thích hợp. Thêm những điều sau vào 'LoggedStream' giải quyết vấn đề:' LoggedStream const & operator << (std :: ostream & (* F) (std :: ostream &)) const {F (out); return * this; } ' –

12

tôi đã quay quanh đầu tôi làm thế nào để làm điều tương tự và tôi phát hiện ra nó thực sự không phải là khó. Về cơ bản chỉ phân lớp ostream và các đối tượng streambuf, và xây dựng ostream với chính nó như là bộ đệm. tràn ảo() từ std :: streambuf sẽ được gọi cho mọi ký tự được gửi tới luồng. Để phù hợp với ví dụ của bạn tôi chỉ cần thực hiện một hàm foo() và gọi nó.

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; 
} 

oh và bỏ qua thực tế là chức năng chính không phải là chức năng chính thực sự. Trong một không gian tên được gọi từ nơi khác, p

+2

Hoạt động hoàn hảo! Nên là câu trả lời được chấp nhận, mặc dù nó là một câu hỏi cũ. – ZXcvbnM

+0

Tôi đồng ý với ZXcvbnM, đây phải là câu trả lời được chấp nhận. Câu trả lời được chấp nhận có chứa một tham chiếu hữu ích nhưng không thực sự cung cấp một giải pháp. Ben cung cấp một giải pháp làm việc đơn giản. +1. – sgbirch

+0

Cuối cùng tôi tìm thấy một giải pháp sau khi tìm kiếm nó trong một thời gian dài, đây sẽ là câu trả lời được chấp nhận như mọi người nói. – user0103

Các vấn đề liên quan