根據前文,ostream類是c++標準輸出流的一個基礎類別,本篇詳細介紹ostream類的主要成員函數用法。
從ostream標頭檔案中擷取一部分關於建構函式的宣告和定義,如下:
public:
//explicit用來防止由建構函式定義的隱式轉換
explicit
basic_ostream(__streambuf_type* __sb)
{ this->init(__sb); }
protected:
basic_ostream()
{ this->init(0); }
#if __cplusplus >= 201103L
// Non-standard constructor that does not call init()
basic_ostream(basic_iostream<_CharT, _Traits>&) { }
basic_ostream(const basic_ostream&) = delete;
basic_ostream(basic_ostream&& __rhs)
: __ios_type()
{ __ios_type::move(__rhs); }
// 27.7.3.3 Assign/swap
basic_ostream& operator=(const basic_ostream&) = delete;
basic_ostream&
operator=(basic_ostream&& __rhs)
{
swap(__rhs);
return *this;
}
可以看到ostream類的預設建構函式是保護型別,而帶引數的建構函式則是公有的,根據public和protected的功能,我們要定義一個ostream物件,必須要在引數中傳入streambuf型別的指標才可以,否則會報編譯錯誤。
一個可用的例子如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
filebuf buf;
if ( buf.open("/proc/self/fd/1", ios::out) == nullptr )
{
cerr << "stdout open failed" << endl;
return -1;
}
ostream out(&buf);
return 0;
}
與istream一樣,因為streambuf型別的建構函式是保護型別,不能直接使用,所以需要使用它的繼承者stringbuf或者filebuf,這裡使用了filebuf,並且我們輸出錯誤資訊沒有使用cout,這裡使用了ostream定義的另外一個範例cerr,會輸出錯誤資訊到標準錯誤輸出。
ostream類與istream類一樣,它的的拷貝建構函式和賦值函數也都是保護型別的,所以ostream是不允許拷貝或者賦值的,所以它也不能直接作為返回型別和引數傳遞,很多時候需要使用參照來進行傳遞。
部分<<操作符函數原型如下:
//過載一系列<<操作符,可以用於讀取變數資料並放入到流緩衝區中
__ostream_type&
operator<<(long __n)
{ return _M_insert(__n); }
__ostream_type&
operator<<(unsigned long __n)
{ return _M_insert(__n); }
__ostream_type&
operator<<(bool __n)
{ return _M_insert(__n); }
__ostream_type&
operator<<(short __n);
__ostream_type&
operator<<(unsigned short __n)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 117. basic_ostream uses nonexistent num_put member functions.
return _M_insert(static_cast<unsigned long>(__n));
}
__ostream_type&
operator<<(int __n);
__ostream_type&
operator<<(unsigned int __n)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 117. basic_ostream uses nonexistent num_put member functions.
return _M_insert(static_cast<unsigned long>(__n));
}
\<<操作符可用於將資料寫入到輸出流中,使用例子如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
filebuf buf;
if ( buf.open("/proc/self/fd/1", ios::out) == nullptr )
{
cerr << "stdout open failed" << endl;
return -1;
}
ostream out(&buf);
int i = 1234;
long long ll = 1234567;
out << i << endl << ll << endl;
return 0;
}
這時我們猜測,命令列標準輸出中應該會輸出兩行數位,編譯後執行結果如下:
1234
1234567
從這裡out變數用法來看,實際上就是實現了標準庫中cout的功能,當然,我是猜測可能是這樣實現的。
/proc/self/fd/1是linux系統中標準輸出檔案,所以開啟這個檔案操作的話,反映在程式執行的過程中,就是直接輸出到標準輸出。
ostream標頭檔案中put函數原型如下:
//往緩衝區中插入一個字元
__ostream_type&
put(char_type __c);
put函數使用例子如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
filebuf buf;
if ( buf.open("/proc/self/fd/1", ios::out) == nullptr )
{
cerr << "stdout open failed" << endl;
return -1;
}
ostream out(&buf);
char c = 'X';
out.put('c').put('=').put(c).put('\n');
return 0;
}
這裡因為put函數返回的是ostream&型別,所以可以連著使用put函數,程式碼編譯後執行結果如下:
[root@mylinux ~]# ./a.out
c=X
[root@mylinux ~]#
ostream的write函數原型如下:
//將__s指標所指向的字串複製出來並插入到緩衝區中,最多插入__n個字元
__ostream_type&
write(const char_type* __s, streamsize __n);
用法如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
filebuf buf;
if ( buf.open("/proc/self/fd/1", ios::out) == nullptr )
{
cerr << "stdout open failed" << endl;
return -1;
}
ostream out(&buf);
if ( !out.good())
{
cerr << "stream buf state is bad" << endl;
return -1;
}
out.write("aaa\n", 4);
return 0;
}
good函數是ostream繼承於父類別ios的一個成員函數,它用來檢查流的狀態是否正常,正常則返回true。
程式碼編譯執行後結果如下:
[root@mylinux ~]# ./a.out
aaa
[root@mylinux ~]#
函數原型如下:
//將資料從緩衝區同步到儲存媒介中
__ostream_type&
flush();
使用方法如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream out("aaa.txt");
if ( !out.good())
{
cerr << "stream buf state is bad" << endl;
return -1;
}
for (int n=0; n<10; ++n)
{
out << n;
//out.flush();
}
while(1);
out.close();
return 0;
}
這裡使用了ofstream型別,它是ostream的一個子類,所以對於flush用法是一樣的,這裡我們先把flush函數呼叫註釋掉,此時去執行程式碼,然後檢視aaa.txt檔案,會發現資料並沒有寫入到檔案中去,然後我們把註釋取消,重新編譯執行後,檢視aaa.txt內容,會看到0123456789已經被寫入到檔案中去。
按照我的理解,ofstream在往檔案中寫入資料時,資料實際上是先寫到緩衝區中,並沒有寫到檔案中去,所以需要呼叫一個flush,來確保資料會從緩衝區寫到輸出裝置,也就是檔案中去。
這裡有一個小插曲,我一開始使用了out << n << endl去往檔案寫資料,發現flush是不起作用的,一直很疑惑,後來看原始碼才發現endl這個操縱運算元,它會呼叫先往緩衝區寫入一個換行符然後再呼叫flush函數,所以flush才會不起作用。
tellp函數原型如下:
//返回當前寫緩衝區位置
pos_type
tellp();
使用例子如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream out("aaa.txt");
if ( !out.good())
{
cerr << "stream buf state is bad" << endl;
return -1;
}
for (int n=0; n<10; ++n)
{
out << n << endl;
out.flush();
}
clog << "current pos is " << out.tellp() <<endl;
out.close();
return 0;
}
從程式碼分析,目前應該是寫到了20的位置,編譯執行,結果如下:
[root@mylinux~]# ./a.out
current pos is 20
[root@mylinux~]#
seekp函數原型如下:
/**
從當前位置開始,跳轉pos個寫位置
*/
__ostream_type&
seekp(pos_type pos);
/**
根據ios_base::seekdir定義的位置,跳轉off個寫位置
*/
__ostream_type&
seekp(off_type off, ios_base::seekdir);
範例如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream out("aaa.txt");
if ( !out.good())
{
cerr << "stream buf state is bad" << endl;
return -1;
}
out << 1234567;
clog << "first pos is " << out.tellp() <<endl;
out.seekp(3);
clog << "second pos is " << out.tellp() <<endl;
out.seekp(1, ios::beg);
clog << "third pos is " << out.tellp() <<endl;
out.close();
return 0;
}
輸出結果如下:
[root@mylinux ~]# ./a.out
first pos is 7
second pos is 3
third pos is 1
[root@mylinux ~]#
到這裡,ostream類的public成員函數就介紹完畢啦,若有不對之處,歡迎指正。