使用具有自己功能的 << 运算符来“覆盖” cout

Posted

技术标签:

【中文标题】使用具有自己功能的 << 运算符来“覆盖” cout【英文标题】:Use << operator with own function to "override" cout 【发布时间】:2020-05-15 12:26:31 【问题描述】:

我知道有很多关于重载

我想做的是(我认为)完全不同(并且可能更简单)。 我有一个带有 2 个线程的控制台应用程序,都在控制台上编写。我想避免它们在控制台中相互碰撞,所以我使用互斥锁来防止一个线程在另一个线程输出时输出,所以我创建了函数:

void print(string s)
    globals::console_mtx.lock();
    cout << s << endl;
    globals::console_mtx.unlock();

无论数据类型如何,我都希望它能够像这样使用:

int i=5;
print << "Some text" << i << endl;

谢谢

【问题讨论】:

【参考方案1】:

这是你的想法吗?:

struct print_t ;
static const print_t print = print_t();

print_t& operator<<(print_t& p, const std::string& s) 
    globals::console_mtx.lock();
    std::cout << s << endl;
    globals::console_mtx.unlock();
    return p;

注意。有大约十几种方法可以使它变得更好,例如将 print 作为具有唯一类型的函数等,以确保您不会遇到静态(取消)初始化顺序失败(如果可能,请避免在静态取消初始化中进行上述操作)。您可以使用 std 中cout 的定义作为备忘单。

【讨论】:

这似乎是我要找的。看到我不必为此费心上课,我松了一口气。谢谢你的帮助。但是,不确定静态去初始化是什么意思 哦,你有一个类,它只是空的 :) - 在 C++ 中你可能会在很多情况下遇到这样的类。 print现在是print_t类型,技术上是static全局变量,所以是在静态初始化的时候创建的(标准里面有规定,基本上,如果你有两个编译单元,顺序是不确定的之间)。因此,在初始化和销毁​​期间,使用它是不安全的,因为它可能已被销毁(尽管没有存储任何内容)。在main()的开头和结尾之间使用是安全的。 @Raphael • 在C++ 中,关于静态和全局变量的构造顺序和破坏顺序没有太多保证。如果你只在main 开始之后和main 结束之前使用构造,你应该没问题。 我明白了。但是为什么我需要将 print 定义为 static ? 您想将其用作cout。如果您不这样定义它,它将不会立即可用。也就是说,你总是可以在你的函数中写print_t print;...【参考方案2】:

重载

print << "Some text" << i << endl;

实际上分别为“Some text”、i 和 endl 调用 print.operator

print << "Other text" << i+1 << endl;

那么你可能会得到:

"Some TextOther Text65\n\n"

处理它最简单的方法是先输出到字符串流,然后使用普通的打印函数打印被锁定的互斥锁保护的整个东西:

stringstream ss;
ss <<"Some text" << i << endl;
print(ss.str());

如果您坚持直接使用流,那么流仅在收到刷新指令时才实际写入来处理此类事情。 endl 自动刷新流,这是它与 "\n" 不同的方式之一。

然后您实现一个自定义 std::stringbuf 锁定互斥锁并在 sync() 上输出到控制台,然后为每个线程构造一个 ostream 以用作其个人打印输出流。

示例代码:

#include <iostream>
#include <sstream>
#include <mutex>
#include <thread>
#include <string>

// Increase risk of race condition if one can be triggered.
char slow_get_ch(char ch)

    for (unsigned int i = 0; i < 10000; ++i)
    
        for (unsigned int j = 0; j < 10000; ++j)
        
        
    

    return ch;


class print_buf : public std::stringbuf

    std::mutex& mtx_;

public:
    print_buf(std::mutex& mtx)
        :
        mtx_(mtx)
    
    

protected:
    int sync() final
    
        std::unique_lock<std::mutex> lck(mtx_);
        std::string val = this->str();
        std::cout << val;
        this->str("");

        return 0;
    
;

void print_worker(std::ostream* print_stream_ptr,char ch)

    std::ostream& print = *print_stream_ptr;

    // Print 5 lines of 20 times ch.
    for (unsigned int i = 0; i < 5; ++i) 
        for (unsigned int j = 0; j < 20; ++j) 
            print << slow_get_ch(ch);
        
        print << std::endl;
    


int main()

    std::mutex print_mutex;
    print_buf buf1(print_mutex);
    print_buf buf2(print_mutex);
    print_buf buf3(print_mutex);

    std::ostream p1(&buf1);
    std::ostream p2(&buf2);
    std::ostream p3(&buf3);

    std::thread t1(print_worker, &p1, 'a');
    std::thread t2(print_worker, &p2, 'b');
    std::thread t3(print_worker, &p3, 'c');

    t1.join();
    t2.join();
    t3.join();

    return 0;

【讨论】:

这很复杂。我最终会使用你的 stringstream 解决方案

以上是关于使用具有自己功能的 << 运算符来“覆盖” cout的主要内容,如果未能解决你的问题,请参考以下文章

具有表达式模板的多维数组模板类

Leetcode 76. 最小覆盖子串

具有特殊功能的拉开功能类型

qtcreator软件编程后结果数字和数字没有间隔怎么回事

通过自定义string类型来理解运算符重载

React Js - 在组件上设置 innerHTML 样式 - 不起作用 - 父样式覆盖子样式