将 std::cout 重定向

23

我需要一个类,在其对象生命期内将一个 ostream 重定向到另一个 ostream。经过一些尝试,我想出了这个:

#include <iostream>
#include <fstream>


class ScopedRedirect
{
public:
    ScopedRedirect(std::ostream & inOriginal, std::ostream & inRedirect) :
        mOriginal(inOriginal),
        mRedirect(inRedirect)
    {
        mOriginal.rdbuf(mRedirect.rdbuf(mOriginal.rdbuf()));
    }

    ~ScopedRedirect()
    {
        mOriginal.rdbuf(mRedirect.rdbuf(mOriginal.rdbuf()));
    }    

private:
    ScopedRedirect(const ScopedRedirect&);
    ScopedRedirect& operator=(const ScopedRedirect&);

    std::ostream & mOriginal;
    std::ostream & mRedirect;
};


int main()
{
    std::cout << "Before redirect." << std::endl;
    std::ofstream filestream("redirected.txt");
    {
        ScopedRedirect redirect(std::cout, filestream);
        std::cout << "During redirect." << std::endl;
    }
    std::cout << "After redirect." << std::endl;

    return 0;
}

它似乎运行良好。但是,奇怪的是下面这行代码在构造函数和析构函数中重复出现:

mOriginal.rdbuf(mRedirect.rdbuf(mOriginal.rdbuf()));

我认为代码是正确的,但我想请SO社区来验证一下。您能在这段代码中找到任何错误或危险吗?

编辑

使其不可复制。


4
+1 - 它应该是正确的 -- 但如果您使用通用的 std::ostream 实现您的逻辑而不是直接调用 std::cout,那将更好。 - Billy ONeal
1
@Billy ONeal: ScopedRedirect 不是已经用通用的 ostream 实现了吗?std::cout 只在示例中使用。 - StackedCrooked
我并不是说你的类是错误或不好的。我只是认为将输出发送到实际想要去的位置比在事后重定向更好。也就是说,我建议依赖于std::cout指向任何特定位置的代码应该进行重构,而不是更改cout指向的位置。 - Billy ONeal
@Billy ONeal:啊,我明白了。我完全同意你的看法。我需要这个类的原因是因为我想关闭一个大型遗留代码库产生的调试消息。这是一个(希望)暂时的解决方案。 - StackedCrooked
1个回答

22
那些行相同的原因是因为你正在交换缓冲区。(也就是说,通过将原始缓冲区与重定向缓冲区交换来“重定向”,恢复时则交换回来。)
尽管这可能会使输出流产生预期效果,但它并不正确,因为重定向流现在输出到其他地方。 重定向 意味着将一个流发送到其他地方输出;请注意这不会影响 '其他地方'。
你的类不是一个重定向类; 就像现在这样,它实际上应该被命名为 ScopedStreamSwap。例如,请尝试使用以下内容:
#include <iostream>
#include <fstream>

class ScopedRedirect
{
public:
    ScopedRedirect(std::ostream & inOriginal, std::ostream & inRedirect) :
        mOriginal(inOriginal),
        mRedirect(inRedirect)
    {
        mOriginal.rdbuf(mRedirect.rdbuf(mOriginal.rdbuf()));
    }

    ~ScopedRedirect()
    {
        mOriginal.rdbuf(mRedirect.rdbuf(mOriginal.rdbuf()));
    }    

private:
    ScopedRedirect(const ScopedRedirect&);
    ScopedRedirect& operator=(const ScopedRedirect&);

    std::ostream & mOriginal;
    std::ostream & mRedirect;
};


int main()
{
    std::cout << "Before redirect." << std::endl;
    std::ofstream filestream("redirected.txt");
    {
        ScopedRedirect redirect(std::cout, filestream);
        std::cout << "During redirect." << std::endl;

        // oops:
        filestream << "also to the file, right?...nope" << std::endl;
        filestream << "ah, why am i on the screen?!" << std::endl;
    }
    std::cout << "After redirect." << std::endl;

    // in main, return 0 is implicit, if there is no return statement;
    // helpful to keep in mind in snippets and short things
}

您需要的是这个:

#include <iostream>
#include <fstream>

class ScopedRedirect
{
public:
    ScopedRedirect(std::ostream & inOriginal, std::ostream & inRedirect) :
        mOriginal(inOriginal),
        mOldBuffer(inOriginal.rdbuf(inRedirect.rdbuf()))
    { }

    ~ScopedRedirect()
    {
        mOriginal.rdbuf(mOldBuffer);
    }    

private:
    ScopedRedirect(const ScopedRedirect&);
    ScopedRedirect& operator=(const ScopedRedirect&);

    std::ostream & mOriginal;
    std::streambuf * mOldBuffer;
};


int main()
{
    std::cout << "Before redirect." << std::endl;
    std::ofstream filestream("redirected.txt");
    {
        ScopedRedirect redirect(std::cout, filestream);
        std::cout << "During redirect." << std::endl;

        // yay:
        filestream << "also to the file, right?...yes" << std::endl;
        filestream << "i am not on the screen" << std::endl;
    }
    std::cout << "After redirect." << std::endl;

    return 0;
}

2
如果翻译有趣且信息量大,或者有幽默的示例文本,则加1分。 - Billy ONeal
感谢您发布一个有效的修复方案。我已经意识到我只是在交换缓冲区,但奇怪的是我无法以不同的方式做到这一点。由于某种原因,语法让我很困惑。 - StackedCrooked

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接