4

我试图通过重载 ofstream 在 C++ 中同时写入文件和标准输出

测试.h

 #pragma once 

#include <iostream>

using  std::ofstream;

class OutputAndConsole:public ofstream
{
public:
    std::string fileName;        
    OutputAndConsole(const std::string& fileName):ofstream(fileName),fileName(fileName){
    };
    template <typename T>
    OutputAndConsole& operator<<(T var);
};


template <typename T>
OutputAndConsole& OutputAndConsole::operator<<(T var)
{
    std::cout << var;
    ofstream::operator << (var);
    return (*this);
};

测试.cpp

  OutputAndConsole file("output.txt");
  file << "test" ;

文件中的输出是

01400930

但在控制台中是

test

我调试了看起来像是正在输入的代码

_Myt& __CLR_OR_THIS_CALL operator<<(const void *_Val)

我究竟做错了什么?

4

2 回答 2

8

我不会评论为什么你的方法不起作用,主要是因为它不能被修补以正常工作。主要问题是你不能使用你的流将它传递给期望的东西std::ostream&并且仍然写入两个流。然而,有一个相对简单但不一定显而易见的方法来实现您真正想要的:您将派生一个新的流缓冲区,即从 派生的类std::streambuf,并覆盖它的overflow()sync()函数。这是一个简单演示的完整代码:

#include <streambuf>

struct teebuf
    : std::streambuf
{
    std::streambuf* sb1_;
    std::streambuf* sb2_;

    teebuf(std::streambuf* sb1, std::streambuf* sb2)
        : sb1_(sb1), sb2_(sb2) {
    }
    int overflow(int c) {
        typedef std::streambuf::traits_type traits;
        bool rc(true);
        if (!traits::eq_int_type(traits::eof(), c)) {
            traits::eq_int_type(this->sb1_->sputc(c), traits::eof())
                && (rc = false);
            traits::eq_int_type(this->sb2_->sputc(c), traits::eof())
                && (rc = false);
        }
        return rc? traits::not_eof(c): traits::eof();
    }
    int sync() {
        bool rc(true);
        this->sb1_->pubsync() != -1 || (rc = false);
        this->sb2_->pubsync() != -1 || (rc = false);
        return rc? 0: -1;
    }
};

#include <fstream>
#include <iostream>

int main()
{
    std::ofstream fout("tee.txt");
    teebuf        sbuf(fout.rdbuf(), std::cout.rdbuf());
    std::ostream  out(&sbuf);
    out << "hello, world!\n";
}

显然,tee-stream 的创建可以很好地打包,但它看起来如何并不重要。重要的是可以为 IOStreams 创建自定义目标(或源),并且不涉及任何从std::ostream. std::ostream从(or )继承的唯一原因std::istream是使用自定义流缓冲区更容易初始化流。

于 2012-12-02T00:28:08.533 回答
6

问题

ofstream::operator << (var);

这是您将其ofstream::operator<<用作合格的函数调用。您要求函数查找定位 ; 的成员函数ofstream。成员的最佳匹配是 for void*,而打印实际字符串内容的专门char*化是一个自由函数(即不是成员函数)。

如果你也这样做cout,你会发现同样的问题:

std::cout.operator<<(var);

解决方案

这可能会做到:

static_cast<ofstream&>(*this) << var;

因为您仍在使用正常的运算符语法(以及这需要的所有重载解决方案),但是使用 aofstream作为 LHS 操作数。

不过,我还没有实际测试过。

结论

顺便说一句, operator<<也应该是一个自由函数,以适应这个约定。

所以:

struct OutputAndConsole : std::ofstream
{
    OutputAndConsole(const std::string& fileName)
       : std::ofstream(fileName)
       , fileName(fileName)
    {};

    const std::string fileName;
};

template <typename T>
OutputAndConsole& operator<<(OutputAndConsole& strm, const T& var)
{
    std::cout << var;
    static_cast<std::ofstream&>(strm) << var;
    return strm;
};

我还冒昧地做了一些小的语法调整。

于 2012-12-02T00:09:23.610 回答