【问题标题】:Use << operator with own function to "override" cout使用具有自己功能的 << 运算符来“覆盖” 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;

谢谢

【问题讨论】:

    标签: c++ operator-overloading cout


    【解决方案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 解决方案
    猜你喜欢
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    • 2019-12-06
    • 2020-03-15
    • 1970-01-01
    • 2016-06-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多