[{"data":1,"prerenderedAt":1028},["ShallowReactive",2],{"page-\u002Fcpp\u002Fstring\u002Fbasic_ostream":3},{"id":4,"title":5,"body":6,"description":43,"extension":1021,"meta":1022,"navigation":1023,"path":1024,"seo":1025,"stem":1026,"__hash__":1027},"content\u002Fcpp\u002Fstring\u002Fbasic_ostream.md","BasicOstream",{"type":7,"value":8,"toc":1017},"minimark",[9,13,17,34,44,55,180,205,211,214,220,223,251,781,794,800,803,809,812,819,825,839,845,848,854,857,863,866,872,875,881,902,905,912,1004,1010,1013],[10,11,12],"h2",{"id":12},"前置知识",[14,15,16],"p",{},"流式一个基础并且核心的概念，是c++用来抽象数据输入和输出操作的概念，可以看做是一个数据传送带或者水管，数据可以从源头输入数据，叫做输入流，可以从程序流出到目的地，叫做输出流。",[14,18,19,20,24,25,29,30,33],{},"它屏蔽了底层硬件设备（键盘、显示器、硬盘、网卡）的差异，让程序员可以用",[21,22,23],"strong",{},"统一的方式","（如 ",[26,27,28],"code",{},"\u003C\u003C"," 和 ",[26,31,32],{},">>"," 操作符）来读写数据，而不用关心数据到底去了哪里。",[35,36,41],"pre",{"className":37,"code":39,"language":40},[38],"language-text","\u002F\u002F 对程序员来说，写法几乎一样：\nstd::cout \u003C\u003C \"Hello\";   \u002F\u002F 流向控制台\nfileStream \u003C\u003C \"Hello\";  \u002F\u002F 流向文件\nsocketStream \u003C\u003C \"Hello\"; \u002F\u002F 流向网络\n","text",[26,42,39],{"__ignoreMap":43},"",[14,45,46,47,50,51,54],{},"我们深入分析的 ",[26,48,49],{},"basic_ostream"," 就是 C++ 标准库流体系的核心基类。整个体系可以看作一个",[21,52,53],{},"继承树","：",[56,57,58,74],"table",{},[59,60,61],"thead",{},[62,63,64,68,71],"tr",{},[65,66,67],"th",{},"层次",[65,69,70],{},"类名",[65,72,73],{},"作用",[75,76,77,93,112,134,152,167],"tbody",{},[62,78,79,85,90],{},[80,81,82],"td",{},[21,83,84],{},"核心基类",[80,86,87],{},[26,88,89],{},"ios_base",[80,91,92],{},"管理流状态、格式标志（如进制、宽度）",[62,94,95,100,105],{},[80,96,97],{},[21,98,99],{},"中间基类",[80,101,102],{},[26,103,104],{},"basic_ios",[80,106,107,108,111],{},"管理流缓冲区指针 ",[26,109,110],{},"rdbuf()","，处理错误状态",[62,113,114,119,124],{},[80,115,116],{},[21,117,118],{},"输入流",[80,120,121],{},[26,122,123],{},"basic_istream",[80,125,126,127,130,131,133],{},"负责",[21,128,129],{},"读","数据（",[26,132,32],{}," 操作符）",[62,135,136,141,145],{},[80,137,138],{},[21,139,140],{},"输出流",[80,142,143],{},[26,144,49],{},[80,146,126,147,130,150,133],{},[21,148,149],{},"写",[26,151,28],{},[62,153,154,159,164],{},[80,155,156],{},[21,157,158],{},"输入输出流",[80,160,161],{},[26,162,163],{},"basic_iostream",[80,165,166],{},"同时支持读写（继承自上述两者）",[62,168,169,176,178],{},[80,170,171,172,175],{},"根据数据的目的地来看，可以将流分为几个大类，底层机制是一样的，",[21,173,174],{},"区别在于内部绑定的 “流缓冲区（streambuf）” 不同","。",[80,177],{},[80,179],{},[14,181,182,183,54,185,188,189,192,193,196,197,200,201,204],{},"数据流入的是磁盘文件的话，就叫做文件流。 ",[21,184,70],{},[26,186,187],{},"ifstream","（读文件）、",[26,190,191],{},"ofstream","（写文件）、",[26,194,195],{},"fstream","（读写文件）\n",[21,198,199],{},"关联","：它内部绑定了一个 ",[26,202,203],{},"filebuf","（文件缓冲区），负责将数据真正写入硬盘。",[35,206,209],{"className":207,"code":208,"language":40},[38],"#include \u003Cfstream>\nstd::ofstream file(\"test.txt\");\nfile \u003C\u003C \"写入文件\";   \u002F\u002F 用法和 cout 完全一样！\nfile.close();\n",[26,210,208],{"__ignoreMap":43},[14,212,213],{},"字符串流是将内存中的字符串上进行读写，用于数据类型转换或者作为内存中的临时存储，避免频繁的IO操作，内部绑定了stringbuf字符串缓冲区。",[35,215,218],{"className":216,"code":217,"language":40},[38],"#include \u003Csstream>\nstd::ostringstream oss;\noss \u003C\u003C \"年龄: \" \u003C\u003C 25;\nstd::string result = oss.str(); \u002F\u002F 把数据“流”进字符串里\n",[26,219,217],{"__ignoreMap":43},[14,221,222],{},"标准流是操作系统默认帮你打开的流，用于连接程序和终端：",[224,225,226,235,243],"ul",{},[227,228,229,234],"li",{},[21,230,231],{},[26,232,233],{},"std::cout","（标准输出流）：通常指向控制台屏幕。",[227,236,237,242],{},[21,238,239],{},[26,240,241],{},"std::cin","（标准输入流）：通常指向键盘。",[227,244,245,250],{},[21,246,247],{},[26,248,249],{},"std::cerr","（标准错误流）：无缓冲，立即输出错误信息。",[35,252,256],{"className":253,"code":254,"language":255,"meta":43,"style":43},"language-cpp shiki shiki-themes github-light github-dark","class basic_ostream : virtual public basic_ios\u003C_Elem, _Traits> { \u002F\u002F control insertions into a stream buffer\npublic:\n    using _Myios = basic_ios\u003C_Elem, _Traits>;\n    using _Mysb  = basic_streambuf\u003C_Elem, _Traits>;\n    using _Iter  = ostreambuf_iterator\u003C_Elem, _Traits>;\n    using _Nput  = num_put\u003C_Elem, _Iter>;\n    explicit __CLR_OR_THIS_CALL basic_ostream(basic_streambuf\u003C_Elem, _Traits>* _Strbuf, bool _Isstd = false) {\n        _Myios::init(_Strbuf, _Isstd);\n    }\n    __CLR_OR_THIS_CALL basic_ostream(_Uninitialized, bool _Addit = true) {\n        if (_Addit) {\n            this->_Addstd(this); \u002F\u002F suppress for basic_iostream\n        }\n    }\nprotected:\n    __CLR_OR_THIS_CALL basic_ostream(basic_ostream&& _Right) noexcept(false) {\n       _Myios::init();\n        _Myios::move(_STD move(_Right));\n    } \n    basic_ostream& __CLR_OR_THIS_CALL operator=(basic_ostream&& _Right) noexcept \u002F* strengthened *\u002F {\n        this->swap(_Right);\n        return *this;\n    }\n    void __CLR_OR_THIS_CALL swap(basic_ostream& _Right) noexcept \u002F* strengthened *\u002F {\n        if (this != _STD addressof(_Right)) {\n            _Myios::swap(_Right);\n        }\n    }\npublic:\n    __CLR_OR_THIS_CALL basic_ostream(const basic_ostream&)            = delete;\n    basic_ostream& __CLR_OR_THIS_CALL operator=(const basic_ostream&) = delete;\n    __CLR_OR_THIS_CALL ~basic_ostream() noexcept override {}\n    using int_type = typename _Traits::int_type;\n    using pos_type = typename _Traits::pos_type;\n    using off_type = typename _Traits::off_type;\n    class _Sentry_base { \u002F\u002F stores thread lock and reference to output stream\n    public:\n        __CLR_OR_THIS_CALL _Sentry_base(basic_ostream& _Ostr) : _Myostr(_Ostr) { \u002F\u002F lock the stream buffer, if there\n            const auto _Rdbuf = _Myostr.rdbuf();\n            if (_Rdbuf) {\n                _Rdbuf->_Lock();\n            }\n        }\n        __CLR_OR_THIS_CALL ~_Sentry_base() noexcept { \u002F\u002F destroy after unlocking\n            const auto _Rdbuf = _Myostr.rdbuf();\n            if (_Rdbuf) {\n                _Rdbuf->_Unlock();\n            }\n        }\n        basic_ostream& _Myostr; \u002F\u002F the output stream, for _Unlock call at destruction\n        _Sentry_base& operator=(const _Sentry_base&) = delete;\n    };\n   class sentry : public _Sentry_base {\n    public:\n        explicit __CLR_OR_THIS_CALL sentry(basic_ostream& _Ostr) : _Sentry_base(_Ostr) {\n            if (!_Ostr.good()) {\n                _Ok = false;\n                return;\n            }\n           const auto _Tied = _Ostr.tie();\n            if (!_Tied || _Tied == _STD addressof(_Ostr)) {\n                _Ok = true;\n                return;\n            }\n            _Tied->flush();\n            _Ok = _Ostr.good(); \u002F\u002F store test only after flushing tie\n        }\n        _STL_DISABLE_DEPRECATED_WARNING\n        __CLR_OR_THIS_CALL ~sentry() noexcept {\n#if !_HAS_EXCEPTIONS\n            const bool _Zero_uncaught_exceptions = true;\n#elif _HAS_DEPRECATED_UNCAUGHT_EXCEPTION\n            const bool _Zero_uncaught_exceptions = !_STD uncaught_exception(); \u002F\u002F TRANSITION, ArchivedOS-12000909\n#else \u002F\u002F ^^^ _HAS_DEPRECATED_UNCAUGHT_EXCEPTION \u002F !_HAS_DEPRECATED_UNCAUGHT_EXCEPTION vvv\n            const bool _Zero_uncaught_exceptions = _STD uncaught_exceptions() == 0;\n#endif \u002F\u002F ^^^ !_HAS_DEPRECATED_UNCAUGHT_EXCEPTION ^^^  \n            if (_Zero_uncaught_exceptions) {\n                this->_Myostr._Osfx();\n            }\n        }\n        _STL_RESTORE_DEPRECATED_WARNING \n        explicit __CLR_OR_THIS_CALL operator bool() const {\n            return _Ok;\n        }\n        __CLR_OR_THIS_CALL sentry(const sentry&)            = delete;\n        sentry& __CLR_OR_THIS_CALL operator=(const sentry&) = delete; \n    private:\n        bool _Ok; \u002F\u002F true if stream state okay at construction\n    };\n","cpp",[26,257,258,266,272,278,284,290,296,302,308,314,320,326,332,338,343,349,355,361,367,373,379,385,391,396,402,408,414,419,424,429,435,441,447,453,459,465,471,477,483,489,495,501,507,512,518,523,528,534,539,544,550,556,562,568,573,579,585,591,597,602,608,614,620,625,630,636,642,647,653,659,665,671,677,683,692,698,707,713,719,724,729,735,741,747,752,758,764,770,776],{"__ignoreMap":43},[259,260,263],"span",{"class":261,"line":262},"line",1,[259,264,265],{},"class basic_ostream : virtual public basic_ios\u003C_Elem, _Traits> { \u002F\u002F control insertions into a stream buffer\n",[259,267,269],{"class":261,"line":268},2,[259,270,271],{},"public:\n",[259,273,275],{"class":261,"line":274},3,[259,276,277],{},"    using _Myios = basic_ios\u003C_Elem, _Traits>;\n",[259,279,281],{"class":261,"line":280},4,[259,282,283],{},"    using _Mysb  = basic_streambuf\u003C_Elem, _Traits>;\n",[259,285,287],{"class":261,"line":286},5,[259,288,289],{},"    using _Iter  = ostreambuf_iterator\u003C_Elem, _Traits>;\n",[259,291,293],{"class":261,"line":292},6,[259,294,295],{},"    using _Nput  = num_put\u003C_Elem, _Iter>;\n",[259,297,299],{"class":261,"line":298},7,[259,300,301],{},"    explicit __CLR_OR_THIS_CALL basic_ostream(basic_streambuf\u003C_Elem, _Traits>* _Strbuf, bool _Isstd = false) {\n",[259,303,305],{"class":261,"line":304},8,[259,306,307],{},"        _Myios::init(_Strbuf, _Isstd);\n",[259,309,311],{"class":261,"line":310},9,[259,312,313],{},"    }\n",[259,315,317],{"class":261,"line":316},10,[259,318,319],{},"    __CLR_OR_THIS_CALL basic_ostream(_Uninitialized, bool _Addit = true) {\n",[259,321,323],{"class":261,"line":322},11,[259,324,325],{},"        if (_Addit) {\n",[259,327,329],{"class":261,"line":328},12,[259,330,331],{},"            this->_Addstd(this); \u002F\u002F suppress for basic_iostream\n",[259,333,335],{"class":261,"line":334},13,[259,336,337],{},"        }\n",[259,339,341],{"class":261,"line":340},14,[259,342,313],{},[259,344,346],{"class":261,"line":345},15,[259,347,348],{},"protected:\n",[259,350,352],{"class":261,"line":351},16,[259,353,354],{},"    __CLR_OR_THIS_CALL basic_ostream(basic_ostream&& _Right) noexcept(false) {\n",[259,356,358],{"class":261,"line":357},17,[259,359,360],{},"       _Myios::init();\n",[259,362,364],{"class":261,"line":363},18,[259,365,366],{},"        _Myios::move(_STD move(_Right));\n",[259,368,370],{"class":261,"line":369},19,[259,371,372],{},"    } \n",[259,374,376],{"class":261,"line":375},20,[259,377,378],{},"    basic_ostream& __CLR_OR_THIS_CALL operator=(basic_ostream&& _Right) noexcept \u002F* strengthened *\u002F {\n",[259,380,382],{"class":261,"line":381},21,[259,383,384],{},"        this->swap(_Right);\n",[259,386,388],{"class":261,"line":387},22,[259,389,390],{},"        return *this;\n",[259,392,394],{"class":261,"line":393},23,[259,395,313],{},[259,397,399],{"class":261,"line":398},24,[259,400,401],{},"    void __CLR_OR_THIS_CALL swap(basic_ostream& _Right) noexcept \u002F* strengthened *\u002F {\n",[259,403,405],{"class":261,"line":404},25,[259,406,407],{},"        if (this != _STD addressof(_Right)) {\n",[259,409,411],{"class":261,"line":410},26,[259,412,413],{},"            _Myios::swap(_Right);\n",[259,415,417],{"class":261,"line":416},27,[259,418,337],{},[259,420,422],{"class":261,"line":421},28,[259,423,313],{},[259,425,427],{"class":261,"line":426},29,[259,428,271],{},[259,430,432],{"class":261,"line":431},30,[259,433,434],{},"    __CLR_OR_THIS_CALL basic_ostream(const basic_ostream&)            = delete;\n",[259,436,438],{"class":261,"line":437},31,[259,439,440],{},"    basic_ostream& __CLR_OR_THIS_CALL operator=(const basic_ostream&) = delete;\n",[259,442,444],{"class":261,"line":443},32,[259,445,446],{},"    __CLR_OR_THIS_CALL ~basic_ostream() noexcept override {}\n",[259,448,450],{"class":261,"line":449},33,[259,451,452],{},"    using int_type = typename _Traits::int_type;\n",[259,454,456],{"class":261,"line":455},34,[259,457,458],{},"    using pos_type = typename _Traits::pos_type;\n",[259,460,462],{"class":261,"line":461},35,[259,463,464],{},"    using off_type = typename _Traits::off_type;\n",[259,466,468],{"class":261,"line":467},36,[259,469,470],{},"    class _Sentry_base { \u002F\u002F stores thread lock and reference to output stream\n",[259,472,474],{"class":261,"line":473},37,[259,475,476],{},"    public:\n",[259,478,480],{"class":261,"line":479},38,[259,481,482],{},"        __CLR_OR_THIS_CALL _Sentry_base(basic_ostream& _Ostr) : _Myostr(_Ostr) { \u002F\u002F lock the stream buffer, if there\n",[259,484,486],{"class":261,"line":485},39,[259,487,488],{},"            const auto _Rdbuf = _Myostr.rdbuf();\n",[259,490,492],{"class":261,"line":491},40,[259,493,494],{},"            if (_Rdbuf) {\n",[259,496,498],{"class":261,"line":497},41,[259,499,500],{},"                _Rdbuf->_Lock();\n",[259,502,504],{"class":261,"line":503},42,[259,505,506],{},"            }\n",[259,508,510],{"class":261,"line":509},43,[259,511,337],{},[259,513,515],{"class":261,"line":514},44,[259,516,517],{},"        __CLR_OR_THIS_CALL ~_Sentry_base() noexcept { \u002F\u002F destroy after unlocking\n",[259,519,521],{"class":261,"line":520},45,[259,522,488],{},[259,524,526],{"class":261,"line":525},46,[259,527,494],{},[259,529,531],{"class":261,"line":530},47,[259,532,533],{},"                _Rdbuf->_Unlock();\n",[259,535,537],{"class":261,"line":536},48,[259,538,506],{},[259,540,542],{"class":261,"line":541},49,[259,543,337],{},[259,545,547],{"class":261,"line":546},50,[259,548,549],{},"        basic_ostream& _Myostr; \u002F\u002F the output stream, for _Unlock call at destruction\n",[259,551,553],{"class":261,"line":552},51,[259,554,555],{},"        _Sentry_base& operator=(const _Sentry_base&) = delete;\n",[259,557,559],{"class":261,"line":558},52,[259,560,561],{},"    };\n",[259,563,565],{"class":261,"line":564},53,[259,566,567],{},"   class sentry : public _Sentry_base {\n",[259,569,571],{"class":261,"line":570},54,[259,572,476],{},[259,574,576],{"class":261,"line":575},55,[259,577,578],{},"        explicit __CLR_OR_THIS_CALL sentry(basic_ostream& _Ostr) : _Sentry_base(_Ostr) {\n",[259,580,582],{"class":261,"line":581},56,[259,583,584],{},"            if (!_Ostr.good()) {\n",[259,586,588],{"class":261,"line":587},57,[259,589,590],{},"                _Ok = false;\n",[259,592,594],{"class":261,"line":593},58,[259,595,596],{},"                return;\n",[259,598,600],{"class":261,"line":599},59,[259,601,506],{},[259,603,605],{"class":261,"line":604},60,[259,606,607],{},"           const auto _Tied = _Ostr.tie();\n",[259,609,611],{"class":261,"line":610},61,[259,612,613],{},"            if (!_Tied || _Tied == _STD addressof(_Ostr)) {\n",[259,615,617],{"class":261,"line":616},62,[259,618,619],{},"                _Ok = true;\n",[259,621,623],{"class":261,"line":622},63,[259,624,596],{},[259,626,628],{"class":261,"line":627},64,[259,629,506],{},[259,631,633],{"class":261,"line":632},65,[259,634,635],{},"            _Tied->flush();\n",[259,637,639],{"class":261,"line":638},66,[259,640,641],{},"            _Ok = _Ostr.good(); \u002F\u002F store test only after flushing tie\n",[259,643,645],{"class":261,"line":644},67,[259,646,337],{},[259,648,650],{"class":261,"line":649},68,[259,651,652],{},"        _STL_DISABLE_DEPRECATED_WARNING\n",[259,654,656],{"class":261,"line":655},69,[259,657,658],{},"        __CLR_OR_THIS_CALL ~sentry() noexcept {\n",[259,660,662],{"class":261,"line":661},70,[259,663,664],{},"#if !_HAS_EXCEPTIONS\n",[259,666,668],{"class":261,"line":667},71,[259,669,670],{},"            const bool _Zero_uncaught_exceptions = true;\n",[259,672,674],{"class":261,"line":673},72,[259,675,676],{},"#elif _HAS_DEPRECATED_UNCAUGHT_EXCEPTION\n",[259,678,680],{"class":261,"line":679},73,[259,681,682],{},"            const bool _Zero_uncaught_exceptions = !_STD uncaught_exception(); \u002F\u002F TRANSITION, ArchivedOS-12000909\n",[259,684,686,689],{"class":261,"line":685},74,[259,687,688],{},"#else",[259,690,691],{}," \u002F\u002F ^^^ _HAS_DEPRECATED_UNCAUGHT_EXCEPTION \u002F !_HAS_DEPRECATED_UNCAUGHT_EXCEPTION vvv\n",[259,693,695],{"class":261,"line":694},75,[259,696,697],{},"            const bool _Zero_uncaught_exceptions = _STD uncaught_exceptions() == 0;\n",[259,699,701,704],{"class":261,"line":700},76,[259,702,703],{},"#endif",[259,705,706],{}," \u002F\u002F ^^^ !_HAS_DEPRECATED_UNCAUGHT_EXCEPTION ^^^  \n",[259,708,710],{"class":261,"line":709},77,[259,711,712],{},"            if (_Zero_uncaught_exceptions) {\n",[259,714,716],{"class":261,"line":715},78,[259,717,718],{},"                this->_Myostr._Osfx();\n",[259,720,722],{"class":261,"line":721},79,[259,723,506],{},[259,725,727],{"class":261,"line":726},80,[259,728,337],{},[259,730,732],{"class":261,"line":731},81,[259,733,734],{},"        _STL_RESTORE_DEPRECATED_WARNING \n",[259,736,738],{"class":261,"line":737},82,[259,739,740],{},"        explicit __CLR_OR_THIS_CALL operator bool() const {\n",[259,742,744],{"class":261,"line":743},83,[259,745,746],{},"            return _Ok;\n",[259,748,750],{"class":261,"line":749},84,[259,751,337],{},[259,753,755],{"class":261,"line":754},85,[259,756,757],{},"        __CLR_OR_THIS_CALL sentry(const sentry&)            = delete;\n",[259,759,761],{"class":261,"line":760},86,[259,762,763],{},"        sentry& __CLR_OR_THIS_CALL operator=(const sentry&) = delete; \n",[259,765,767],{"class":261,"line":766},87,[259,768,769],{},"    private:\n",[259,771,773],{"class":261,"line":772},88,[259,774,775],{},"        bool _Ok; \u002F\u002F true if stream state okay at construction\n",[259,777,779],{"class":261,"line":778},89,[259,780,561],{},[14,782,783,784,786,787,29,789,791,792,175],{},"虚继承自basic_ios，",[26,785,163],{}," 同时继承 ",[26,788,123],{},[26,790,49],{},"，而两者都继承自 ",[26,793,104],{},[35,795,798],{"className":796,"code":797,"language":40},[38],"            basic_ios\n          \u002F          \\\n   basic_istream   basic_ostream   ← 这里是虚继承\n          \\          \u002F\n           basic_iostream\n",[26,799,797],{"__ignoreMap":43},[14,801,802],{},"类型别名：",[35,804,807],{"className":805,"code":806,"language":40},[38],"using _Myios = basic_ios\u003C_Elem, _Traits>;   \u002F\u002F 基类\nusing _Mysb  = basic_streambuf\u003C_Elem, _Traits>;  \u002F\u002F 流缓冲区\nusing _Iter  = ostreambuf_iterator\u003C_Elem, _Traits>;  \u002F\u002F 输出迭代器\nusing _Nput  = num_put\u003C_Elem, _Iter>;  \u002F\u002F 数值格式化器\n",[26,808,806],{"__ignoreMap":43},[14,810,811],{},"这里实现不同类型的构造：",[14,813,814,815,818],{},"绑定流缓冲区，将",[21,816,817],{},"输出流绑定到一个具体的流缓冲区","（文件、字符串、控制台等）",[35,820,823],{"className":821,"code":822,"language":40},[38],"explicit basic_ostream(basic_streambuf\u003C_Elem, _Traits>* _Strbuf, bool _Isstd = false) {\n    _Myios::init(_Strbuf, _Isstd);\n}\n",[26,824,822],{"__ignoreMap":43},[14,826,827,828,830,831,834,835,838],{},"特殊构造，未初始化状态， 用于 ",[26,829,163],{}," 这样的派生类，它们需要",[21,832,833],{},"先构造","，再初始化基类， ",[26,836,837],{},"_Uninitialized"," 是一个空标记类型，表示\"不初始化流缓冲区\"。",[35,840,843],{"className":841,"code":842,"language":40},[38],"basic_ostream(_Uninitialized, bool _Addit = true) {\n    if (_Addit) {\n        this->_Addstd(this);  \u002F\u002F 注册到标准流列表\n    }\n}\n",[26,844,842],{"__ignoreMap":43},[14,846,847],{},"移动构造，支持移动语义，避免不必要拷贝，移动可能需要抛出异常，这个取决流缓冲区：",[35,849,852],{"className":850,"code":851,"language":40},[38],"basic_ostream(basic_ostream&& _Right) noexcept(false) {\n    _Myios::init();           \u002F\u002F 先初始化基类（无缓冲区）\n    _Myios::move(_STD move(_Right));  \u002F\u002F 移动基类资源\n}\n",[26,853,851],{"__ignoreMap":43},[14,855,856],{},"由于流对象不可拷贝，这是设计上的选择，std::cout不能拷贝，因此我们需要删除拷贝操作",[35,858,861],{"className":859,"code":860,"language":40},[38],"basic_ostream(const basic_ostream&) = delete;\nbasic_ostream& operator=(const basic_ostream&) = delete;\n",[26,862,860],{"__ignoreMap":43},[14,864,865],{},"内部有一个叫做Sentry的对象，是流操作的防护，每次执行\u003C\u003C操作的时候需要检查流状态是否good，刷新tie，如果有绑定的输入流，先刷新，加锁在多线程的环境下保护流缓冲区，还可以解析的时候解锁刷新确保数据输出。",[35,867,870],{"className":868,"code":869,"language":40},[38],"class _Sentry_base {  \u002F\u002F 基类：负责加锁\u002F解锁\npublic:\n    _Sentry_base(basic_ostream& _Ostr) : _Myostr(_Ostr) {\n        const auto _Rdbuf = _Myostr.rdbuf();\n        if (_Rdbuf) {\n            _Rdbuf->_Lock();  \u002F\u002F ← 加锁！\n        }\n    }\n    \n    ~_Sentry_base() noexcept {\n        const auto _Rdbuf = _Myostr.rdbuf();\n        if (_Rdbuf) {\n            _Rdbuf->_Unlock();  \u002F\u002F ← 解锁！\n        }\n    }\n    \n    basic_ostream& _Myostr;\n};\n\nclass sentry : public _Sentry_base {\npublic:\n    explicit sentry(basic_ostream& _Ostr) : _Sentry_base(_Ostr) {\n        \u002F\u002F 1. 检查流状态\n        if (!_Ostr.good()) {\n            _Ok = false;\n            return;\n        }\n        \n        \u002F\u002F 2. 刷新绑定的输入流（tie）\n        const auto _Tied = _Ostr.tie();\n        if (!_Tied || _Tied == &_Ostr) {\n            _Ok = true;\n            return;\n        }\n        \n        _Tied->flush();  \u002F\u002F 刷新绑定的流\n        _Ok = _Ostr.good();\n    }\n    \n    ~sentry() noexcept {\n        \u002F\u002F 析构时调用 _Osfx()（输出刷新和清理）\n        if (_Zero_uncaught_exceptions) {\n            this->_Myostr._Osfx();\n        }\n    }\n    \n    explicit operator bool() const { return _Ok; }  \u002F\u002F 检查是否成功\n    \nprivate:\n    bool _Ok;\n};\n",[26,871,869],{"__ignoreMap":43},[10,873,874],{"id":874},"模板构造",[35,876,879],{"className":877,"code":878,"language":40},[38],"#include \u003Cfmt\u002Fformat.h>\n\ntemplate\u003Ctypename CharT, typename Traits>\nstd::basic_ostream\u003CCharT, Traits>& operator\u003C\u003C(\n    std::basic_ostream\u003CCharT, Traits>& os,\n    const Person& p\n) {\n    os \u003C\u003C \"Person{name: \" \u003C\u003C p.name \n       \u003C\u003C \", age: \" \u003C\u003C p.age \n       \u003C\u003C \", hobbies: [\"\n       \u003C\u003C fmt::join(p.hobbies, \", \")\n       \u003C\u003C \"]}\";\n    return os;\n}\n",[26,880,878],{"__ignoreMap":43},[14,882,883,884,29,887,890,891,893,894,897,898,901],{},"这里是个利用函数模板和fmt库实现的自定义数据结构的流输出操作。",[26,885,886],{},"CharT",[26,888,889],{},"Traits"," 是 C++ 标准库流设计的核心，理解了它们，你就理解了 ",[26,892,49],{}," 为什么能同时支持 ",[26,895,896],{},"char","、",[26,899,900],{},"wchar_t"," 甚至自定义字符类型。",[903,904],"hr",{},[14,906,907,908,911],{},"CharT是字符类型，决定了流中",[21,909,910],{},"每个字符用什么类型","去存储：",[56,913,914,928],{},[59,915,916],{},[62,917,918,921,925],{},[65,919,920],{},"实例化",[65,922,923],{},[26,924,886],{},[65,926,927],{},"说明",[75,929,930,944,958,974,989],{},[62,931,932,937,941],{},[80,933,934],{},[26,935,936],{},"std::ostream",[80,938,939],{},[26,940,896],{},[80,942,943],{},"单字节 ASCII\u002FUTF-8",[62,945,946,951,955],{},[80,947,948],{},[26,949,950],{},"std::wostream",[80,952,953],{},[26,954,900],{},[80,956,957],{},"宽字符（Windows 上 2 字节，Linux 上 4 字节）",[62,959,960,966,971],{},[80,961,962,965],{},[26,963,964],{},"std::u8ostream"," (C++20)",[80,967,968],{},[26,969,970],{},"char8_t",[80,972,973],{},"UTF-8 字符",[62,975,976,981,986],{},[80,977,978,965],{},[26,979,980],{},"std::u16ostream",[80,982,983],{},[26,984,985],{},"char16_t",[80,987,988],{},"UTF-16 字符",[62,990,991,996,1001],{},[80,992,993,965],{},[26,994,995],{},"std::u32ostream",[80,997,998],{},[26,999,1000],{},"char32_t",[80,1002,1003],{},"UTF-32 字符",[35,1005,1008],{"className":1006,"code":1007,"language":40},[38],"\u002F\u002F 1. char 版本\nstd::ostream& os = std::cout;      \u002F\u002F CharT = char\nos \u003C\u003C \"Hello\";                     \u002F\u002F 存储 char 序列\n\n\u002F\u002F 2. wchar_t 版本\nstd::wostream& wos = std::wcout;   \u002F\u002F CharT = wchar_t\nwos \u003C\u003C L\"Hello\";                   \u002F\u002F 存储 wchar_t 序列\n\n\u002F\u002F 3. 模板函数自动适配\ntemplate\u003Ctypename CharT, typename Traits>\nvoid print(std::basic_ostream\u003CCharT, Traits>& os, const CharT* str) {\n    os \u003C\u003C str;  \u002F\u002F 自动适配 char 或 wchar_t\n}\n\nprint(std::cout, \"Hello\");     \u002F\u002F CharT = char\nprint(std::wcout, L\"Hello\");   \u002F\u002F CharT = wchar_t\n",[26,1009,1007],{"__ignoreMap":43},[14,1011,1012],{},"Traits是策略类，用于定义字符类型CharT的各种操作和行为，比如说比较两个字符，计算字符串长度，EOF值是多少，怎么复制字符数组。",[1014,1015,1016],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":43,"searchDepth":268,"depth":268,"links":1018},[1019,1020],{"id":12,"depth":268,"text":12},{"id":874,"depth":268,"text":874},"md",{},true,"\u002Fcpp\u002Fstring\u002Fbasic_ostream",{"description":43},"cpp\u002Fstring\u002Fbasic_ostream","QBxxNG4Vzlv_YvZ2sF-BJG6m6p1Gt-eGPljDHm7fpRQ",1791042464229]