使用<<运算符写入文件和cout

时间:2014-06-25 16:13:24

标签: c++ file operator-overloading cout

我想重载<<运算符写入文件和cout所需的值。我试图用下面的代码来做,但无法成功。它只是将值写入文本文件。任何帮助,将不胜感激。谢谢。

void operator<<(std::ostream& os, const string& str)
{
    std::cout << str;
    os << str;
}

int main() {
    ofstream fl;
    fl.open("test.txt");
    fl << "!!!Hello World!!!" << endl;
    return 0;
}

6 个答案:

答案 0 :(得分:8)

创建一个辅助类和重载运算符,负责将流传输到两个流。使用辅助类而不是尝试覆盖重载的operator<<函数的标准库实现。

这应该有效:

#include <iostream>
#include <fstream>

struct MyStreamingHelper
{
    MyStreamingHelper(std::ostream& out1,
                      std::ostream& out2) : out1_(out1), out2_(out2) {}
    std::ostream& out1_;
    std::ostream& out2_;
};

template <typename T>
MyStreamingHelper& operator<<(MyStreamingHelper& h, T const& t)
{
   h.out1_ << t;
   h.out2_ << t;
   return h;
}

MyStreamingHelper& operator<<(MyStreamingHelper& h, std::ostream&(*f)(std::ostream&))
{
   h.out1_ << f;
   h.out2_ << f;
   return h;
}

int main()
{
   std::ofstream fl;
   fl.open("test.txt");
   MyStreamingHelper h(fl, std::cout);
   h << "!!!Hello World!!!" << std::endl;
   return 0;
}

答案 1 :(得分:3)

要实现完整的流接口,您应该构建流缓冲区和流:

#include <ostream>
#include <sstream>
#include <streambuf>
#include <vector>

// BasicMultiStreamBuffer
// ============================================================================

template<class Char, class Traits = std::char_traits<Char>, class Allocator = std::allocator<Char> >
class BasicMultiStreamBuffer : public std::basic_stringbuf<Char, Traits, Allocator>
{
    // Types
    // =====

    private:
    typedef typename std::basic_stringbuf<Char, Traits> Base;

    public:
    typedef typename std::basic_streambuf<Char, Traits> buffer_type;
    typedef typename buffer_type::char_type char_type;
    typedef typename buffer_type::traits_type traits_type;
    typedef typename buffer_type::int_type int_type;
    typedef typename buffer_type::pos_type pos_type;
    typedef typename buffer_type::off_type off_type;

    private:
    typedef typename std::vector<buffer_type*> container_type;

    public:
    typedef typename container_type::size_type size_type;
    typedef typename container_type::value_type value_type;
    typedef typename container_type::reference reference;
    typedef typename container_type::const_reference const_reference;
    typedef typename container_type::iterator iterator;
    typedef typename container_type::const_iterator const_iterator;


    // Construction/Destructiion
    // =========================

    public:
    BasicMultiStreamBuffer()
    {}

    BasicMultiStreamBuffer(buffer_type* a) {
        if(a) {
            m_buffers.reserve(1);
            m_buffers.push_back(a);
        }
    }

    template <typename Iterator>
    BasicMultiStreamBuffer(Iterator first, Iterator last)
    :   m_buffers(first, last)
    {}

    ~BasicMultiStreamBuffer() {
        sync();
    }


    private:
    BasicMultiStreamBuffer(BasicMultiStreamBuffer const&); // No Copy.
    BasicMultiStreamBuffer& operator=(BasicMultiStreamBuffer const&); // No Copy.


    // Capacity
    // ========

    public:
    bool empty() const { return m_buffers.empty(); }
    size_type size() const { return m_buffers.size(); }


    // Iterator
    // ========

    public:
    iterator begin() { return m_buffers.begin(); }
    const_iterator begin() const { return m_buffers.end(); }
    iterator end() { return m_buffers.end(); }
    const_iterator end() const { return m_buffers.end(); }


    // Modifiers
    // =========

    public:
    void insert(buffer_type* buffer) {
        if(buffer) m_buffers.push_back(buffer);
    }

    void erase(buffer_type* buffer) {
        iterator pos = this->begin();
        for( ; pos != this->end(); ++pos) {
            if(*pos == buffer) {
                m_buffers.erase(pos);
                break;
            }
        }
    }


    // Synchronization
    // ===============

    protected:
    virtual int sync() {
        int result = 0;
        if( ! m_buffers.empty()) {
            char_type* p = this->pbase();
            std::streamsize n = this->pptr() - p;
            if(n) {
                const_iterator pos = m_buffers.begin();
                for( ; pos != m_buffers.end(); ++pos) {
                    std::streamoff offset = 0;
                    while(offset < n) {
                        int k = (*pos)->sputn(p + offset, n - offset);
                        if(0 <= k) offset += k;
                        else {
                            result = -1;
                            break;
                        }
                    }
                    if((*pos)->pubsync() == -1) result = -1;
                }
                this->setp(this->pbase(), this->epptr());
            }
        }
        if(Base::sync() == -1) result = -1;
        return result;
    }

    private:
    container_type m_buffers;
};

typedef BasicMultiStreamBuffer<char> OStreamBuffers;


// BasicMultiStream
// ============================================================================

template<class Char, class Traits = std::char_traits<Char>, class Allocator = std::allocator<Char> >
class BasicMultiStream : public std::basic_ostream<Char, Traits>
{
    // Types
    // =====

    private:
    typedef std::basic_ostream<Char, Traits> Base;

    public:
    typedef BasicMultiStreamBuffer<Char, Traits, Allocator> multi_buffer;
    typedef std::basic_ostream<Char, Traits> stream_type;

    typedef typename multi_buffer::buffer_type buffer_type;
    typedef typename multi_buffer::char_type char_type;
    typedef typename multi_buffer::traits_type traits_type;
    typedef typename multi_buffer::int_type int_type;
    typedef typename multi_buffer::pos_type pos_type;
    typedef typename multi_buffer::off_type off_type;

    typedef typename multi_buffer::size_type size_type;
    typedef typename multi_buffer::value_type value_type;
    typedef typename multi_buffer::reference reference;
    typedef typename multi_buffer::const_reference const_reference;
    typedef typename multi_buffer::iterator iterator;
    typedef typename multi_buffer::const_iterator const_iterator;


    // Construction
    // ============

    public:
    BasicMultiStream()
    :   Base(&m_buffer)
    {}
    BasicMultiStream(stream_type& stream)
    :   Base(&m_buffer), m_buffer(stream.rdbuf())
    {}

    template <typename StreamIterator>
    BasicMultiStream(StreamIterator& first, StreamIterator& last)
    :   Base(&m_buffer)
    {
        while(first != last) insert(*first++);
    }

    private:
    BasicMultiStream(const BasicMultiStream&); // No copy.
    const BasicMultiStream& operator = (const BasicMultiStream&); // No copy.

    // Capacity
    // ========

    public:
    bool empty() const { return m_buffer.empty(); }
    size_type size() const { return m_buffer.size(); }


    // Iterator
    // ========

    public:
    iterator begin() { return m_buffer.begin(); }
    const_iterator begin() const { return m_buffer.end(); }
    iterator end() { return m_buffer.end(); }
    const_iterator end() const { return m_buffer.end(); }


    // Modifiers
    // =========

    public:
    void insert(stream_type& stream) { m_buffer.insert(stream.rdbuf()); }
    void erase(stream_type& stream) { m_buffer.erase(stream.rdbuf()); }

    private:
    multi_buffer m_buffer;
};

typedef BasicMultiStream<char> MultiStream;


// Test
// =============================================================================

#include <iostream>
int main() {
    MultiStream out;
    out.insert(std::cout);
    out.insert(std::clog);
    out << "Hello\n";
}

这里,输出缓冲在字符串流缓冲区中并与目标流同步。

答案 2 :(得分:1)

您重载的功能位于std::ostream&的左侧和const std::string&的右侧。这就是你调用函数的方法:

fl << "!!!Hello World!!!" << endl;

左手边完美匹配,但右手边没有。您传递的字符串不是std::string,而是char const*类型的对象。即使从char const*const std::string&之间存在可行的转换,也不会调用您所做的自定义重载,因为事实上在其他地方存在完美匹配

完美匹配的重载是命名空间std中发现的重载,其签名为:

namespace std { // Simplification:
    std::ostream& operator<<(std::ostream& os, char const* str);
}

这是一个更好的匹配,因为不需要对右手参数进行转换。这种方法的另一个原因是因为ADL或Argument Dependent Lookup。通常,您必须从这些名称空间外部明确限定来自其他名称空间(如std)的名称或函数,但是当涉及到ADL时,如果编译器可以找到一个采用一类用户定义类型的函数从同一个命名空间调用所述函数不需要从该命名空间外部进行显式限定。因此,上述内容相当于:

std::operator<<(f1, "!!!Hello World!!!") << std::endl;

使用std::getline()时可以看到这一点。即使我们不使用using namespace stdusing std::getline

,以下内容仍然合理
getline(std::cin, line);

由于std::cin与函数std::getline()位于同一名称空间内,因此您无需在函数调用中附加std::

因此,要调用您的重载,必须有更好的匹配。您可以通过明确地创建std::string来强制执行此操作:

fl << std::string("!!!Hello World!!!") << endl;

您的重载被调用而不是名称空间std中的重载,因为封闭名称空间中的重载在std之类的外部名称之前被考虑。但这不仅是非直观的,还会引起其他问题。

  1. 您的函数需要返回std::ostream&类型而不是void并且有一个return os语句,以便您可以链接<< endl表达式。

  2. 在您的函数中,您在os << str行上执行无限递归。有很多方法可以解决此问题,最简单的方法是os << str.c_str(),以便调用名称空间char const*中的std重载。

  3. 您的方法不是最好的方法,所以对于更完整和更好的解决方案,请查看此主题中的其他答案和评论。

答案 3 :(得分:1)

如果你能够使用它,你会发现,毫不奇怪,升级库已经为你完成了大部分的艰苦工作。

#include <iostream>
#include <fstream>
#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/stream.hpp>

typedef boost::iostreams::tee_device<std::ostream, std::ostream> teedev;
typedef boost::iostreams::stream<teedev, std::char_traits<typename std::ostream::char_type>, std::allocator< typename std::ostream::char_type > > tee_stream;

int main(int argc, char* argv[])
{
    std::ofstream of;
    of.open( "test.txt" );

    teedev td( of, std::cout );
    tee_stream ts(td);

    ts << "!!!Hello World!!!" << std::endl;

    return 0;
}

答案 4 :(得分:0)

它只是将值写入文本文件,因为您的运算符&lt;&lt;方法不会被调用。

运营商&lt;&lt;应该返回对流(ostream&amp;)的引用,因为否则&lt;&lt; str&lt;&lt; ENDL;不行。

您的运营商的另一个问题&lt;&lt; os&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;海峡;具有与fl&lt;&lt; &#34;串&#34 ;;

答案 5 :(得分:0)

通常的做法是使用过滤streambuf, 它转发到两个目标streambufs。就像是 以下应该可以解决问题:

class LoggingStreambuf : public std::streambuf
{
    std::streambuf* myPrinciple;
    std::ostream*   myOwner;
    std::filebuf    myLogging;
protected:
    int overflow( int ch ) override
    {
        myLogging->sputc( ch );
        return myPrinciple->sputc( ch );
    }
public:
    LoggingStreambuf( std::streambuf* principal, std::string const& logFileName )
        : myPrinciple( principal )
        , myOwner( nullptr )
        , myLogging( logFileName )
    {
    }

    LoggingStreambuf( std::ostream& principal, std::string const& logFileName )
        : myPrinciple( principal.rdbuf() )
        , myOwner( &principal )
        , myLogging( logFileName )
    {
        myOwner.rdbuf( this );
    }

    ~LoggingStreambuf()
    {
        if ( myOwner != nullptr ) {
            myOwner.rdbuf( myPrinciple );
        }
    }
};

(此特定代码假设文件输出是日志 文件,应忽略错误的辅助输出。 其他错误处理策略也可以实现,以及 当然,没有理由你不能提供两个 任意std::streambuf*,并创建一个新的std::ostream 通过自定义streambuf的实例输出。)

要使用此类,请按以下方式编写:

LoggingStreambuf logger( std::cout, "logfile.txt" );
//  And output to std::cout as usual...

更一般地,当然:任何时候你想做某事 特别关于数据接收器或流的来源,您 实现一个新的std::streambuf,因为这是一个类 处理流的数据下沉和采购。