本文主要介紹cout物件的很多方法,這些方法是機器重要的,是cout可以完成輸出任務的關鍵祕訣。
insertion operator
所有過載版本的返回值型別都是ostream &
<<
預設含義是按位元左移運算子。但是由於他長得像資訊流動,所以就被ostream類發現了,並把它過載了,他就獲得了一份新工作,一個新角色。
並且ostream類過載的非常詳細,<<
可以識別到C++的所有基本數據型別(14個,11個整型,3個浮點型),即C++給這些數據型別的每一個都提供了一個過載版本。
比如:
cout << 34;//ostream & operator<<(int);
把14中內建基本型別的值都可以轉換爲文字形式,比如把-2.45轉換爲5個字元,’-’, ‘2’, ‘.’, ‘4’, ‘5’
char陣列名,顯式char指針,用引號括起來的C字串都是字串指針,因爲C++就是用字串指針來表示字串的。
除了字串指針之外的指針,C++就認爲是void *
,就會列印地址,即指針變數中存的值,而不是列印字串了。
如果你不想列印字串,而是想列印出字串的地址,那就強制轉換爲void *
就好啦。
void *
列印字串的地址#include <iostream>
int main()
{
using std::cout;
using std::endl;
int eggs = 12;
char * amount = "dozen";
cout << &eggs << endl;//列印地址
cout << amount << endl;//列印字串
cout << (void *) amount << endl;//列印地址
return 0;
}
0x6efef8
dozen
0x4ba025
ostream &
插入運算子的過載版本返回的都是呼叫他們的物件
ostream & put(char);
後來爲了用於wchar_t, 把他變成了模板函數,即參數可以是char,可以是wchar_t.
cout.put('i').put('t');
basic_ostream<charT, traits> & write(const char_type * s, streamsize n);//第一個參數是字串的地址,第二個參數是要顯示的字元的數目
#include <iostream>
#include <cstring>
int main()
{
using std::cout;
using std::endl;
const char * state1 = "Florida";
const char * state2 = "Kansas";
const char * state3 = "Euphoria";
int len = std::strlen(state2);
cout << "Increasing loop index:\n";
int i;
for (i=1;i<=len;++i){
cout.write(state2, i);
cout << endl;
}
cout << "Decreasing loop index:\n";
for (i=len;i>=1;--i){
cout.write(state2, i);
cout << endl;
}
cout.write(state2, len + 10) << endl;
return 0;
}
state1, state3兩個字串只是爲了便於觀察長度超出state2的長度時會顯示什麼。
這幾個C字串都存在一起的誒,地址連着的。
Increasing loop index:
K
Ka
Kan
Kans
Kansa
Kansas
Decreasing loop index:
Kansas
Kansa
Kans
Kan
Ka
K
Kansas Euphoria
cout.write(state2, len + 20) << endl;
輸出是:
Kansas Euphoria Index
可以看到,記憶體中有啥就輸出啥了
cout.write(state2, len + 10) << endl;
#include <iostream>
int main()
{
long val = 560031841;
cout.write((char *) &val, sizeof(long));
return 0;
}
aha!
Process returned 0 (0x0) execution time : 0.575 s
Press any key to continue.
560031841轉換爲32二進制是(我的計算機上,long佔4個位元組):
00100001 01100001 01101000 01100001
分別轉換爲是十進制,對應於:33 97 104 97
把他們當做ascii碼,倒着看就是aha!
控制符 manipulator
之前說過了,C++使用緩衝區來匹配不同的傳輸速率,但是如果輸出不是輸出到檔案,而是輸出到螢幕,則其實速率差別並不大,而且我們也絕對不想等到緩衝區的512位元組都滿了,才全部一起顯示到螢幕呀,畢竟我們需要看輸出的資訊。
所以輸出到螢幕的話,重新整理輸出緩衝區的條件不是緩衝區滿,而是使用控制符,C++定義了兩個控制符用來強行重新整理緩衝區,即把緩衝區的所有數據轉移到螢幕:flush和endl。
其實大多數場景都是希望在輸入即將發生時輸出緩衝區,因爲一般輸入都需要提前輸出對應的輸入提示資訊。
#include <iostream>
#include <cstring>
int main()
{
using std::cout;
using std::endl;
cout << "Hello good-looking! " << std::flush;
cout << "Wait just a moment, please." << endl;
cout << "Hello good-looking! " << std::flush;
return 0;
}
Hello good-looking! Wait just a moment, please.
Hello good-looking!
cout << flush;//相當於flush(cout);
這是因爲ostream類對<<
進行了過載,當參數爲函數物件時,就呼叫這個函數並把cout作爲參數。
ostream是ios_base類的間接基礎類別(ios_base是ios類的基礎類別,ios類是ostream類的基礎類別),所以ostream類的物件cout可以使用ios_base類的方法。
#include <iostream>
int main()
{
using std::cout;
cout << "12345678901234567890\n";
char ch = 't';
int t = 123;
cout << ch << ":\n";
cout << t << ":\n";
cout << -t << ":\n";
double f1 = 1.200;
cout << f1 << ":\n";
cout << (f1+1.0/9.0) << ":\n";
double f2 = 1.57e2;
cout << f2 << ":\n";
f2 += 1.0/9.0;
cout << f2 << ":\n";
cout << (f2*1.0e4) << ":\n";
double f3 = 2.3e-4;
cout << f3 << ":\n";
cout << f3/10 << ":\n";
return 0;
}
加個冒號是爲了看欄位寬度的。
可以看到,1.200末尾的0沒有被顯示。這是因爲,C++預設不顯示浮點數結尾的0。
12345678901234567890
t:
123:
-123:
1.2:
1.31111:
157:
157.111:
1.57111e+006:
0.00023:
2.3e-005:
//設定cout物件的計數系統格式狀態爲十六進制,即以十六進制來列印整數
hex(cout);//相當於cout << hex;
和剛纔的flush一樣,控制符是函數。但是注意,不是成員函數哈。
#include <iostream>
int main()
{
using std::cout;
int n = 15;
cout << n << ' ' << n*n << " decimal\n";
//十六進制
cout << std::hex;
cout << n << ' ' << n*n << " hexadecimal\n";
//八進制
cout << std::oct << n << ' ' << n*n << " octal\n";
//回到十進制
dec(cout);
cout << n << ' ' << n*n << " decimal\n";
return 0;
}
15 225 decimal
f e1 hexadecimal
17 341 octal
15 225 decimal
欄位寬度是指一個字串的最大長度,如果設定爲10, 則就只能顯示最多10個字元,但是如果字串多於10個字元,C++當然不會給你截短,只顯示前10個字元,沒那麼僵硬,而是會自動增長欄位長度,然後完整的顯示字串。
#include <iostream>
int main()
{
using std::cout;
int w = cout.width(30);
cout << "default field width = " << w << ":\n";
cout.width(5);
cout << "N" << ":\n";
cout << "N * N" << ":\n";
for (long i=1;i<=100;i *= 10){
cout.width(5);
cout << i << ':';
cout.width(8);
cout << i*i << ":\n";
}
return 0;
}
這是因爲C++會增長欄位,以完整地容納數據。
用來填充的字元叫做填充字元,fill character, 這裏是空格。
default field width = 0:
N:
N * N:
1: 1:
10: 100:
100: 10000:
如果想要左對齊,可以用left控制符
#include <iostream>
int main()
{
using std::cout;
cout << std::left;
int w = cout.width(30);
cout << "default field width = " << w << ":\n";
cout.width(5);
cout << "N" << ":\n";
cout << "N * N" << ":\n";
for (long i=1;i<=100;i *= 10){
cout.width(5);
cout << i << ':';
cout.width(8);
cout << i*i << ":\n";
}
return 0;
}
default field width = 0:
N :
N * N:
1 :1 :
10 :100 :
100 :10000 :
但是fill()不同於width(), 它一旦設定了,就一直有效。
#include <iostream>
int main()
{
using std::cout;
int w = cout.width(30);
cout.fill('-');
cout << "default field width = " << w << ":\n";
cout.width(5);
cout << "N" << ":\n";
cout << "N * N" << ":\n";
for (long i=1;i<=100;i *= 10){
cout.width(5);
cout << i << ':';
cout.width(8);
cout << i*i << ":\n";
}
return 0;
}
--------default field width = 0:
----N:
N * N:
----1:-------1:
---10:-----100:
--100:---10000:
#include <iostream>
int main()
{
using std::cout;
float price1 = 20.40;
float price2 = 1.9 + 8.0/9.0;
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
return 0;
}
可以看到,第三行連小數點都不顯示了,因爲只讓顯示2位。這裏的截短不是C++做的,是程式設計師自己要截短的,怪不着C++,C++沒有私自背地裏偷摸摸的給截短。
"Furry Friends" is $20.4!
"Fiery Friends" is $2.78889!
"Furry Friends" is $20!
"Fiery Friends" is $2.8!
要怎麼進入科學輸出模式和定點輸出模式呢??
——使用控制符fixed和scientific!
定點模式
#include <iostream>
int main()
{
using std::cout;
float price1 = 20.40;
float price2 = 1.9 + 8.0/9.0;
cout << std::fixed;//定點輸出模式
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
return 0;
}
"Furry Friends" is $20.400000!
"Fiery Friends" is $2.788889!
"Furry Friends" is $20.40!
"Fiery Friends" is $2.79!
科學模式:
#include <iostream>
int main()
{
using std::cout;
float price1 = 20.40;
float price2 = 1.9 + 8.0/9.0;
cout << std::scientific;//定點輸出模式
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
return 0;
}
"Furry Friends" is $2.040000e+001!
"Fiery Friends" is $2.788889e+000!
"Furry Friends" is $2.04e+001!
"Fiery Friends" is $2.79e+000!
從上面的範例可以看到,C++預設是不會顯示末尾的0,如果精度太少,則也不會顯示小數點。
但是有時候,比如顯示賬目,把小數點和0都顯示出來更加美觀。但是iostream類沒有提供相關方法,ios_base類提供了。
注意showpoint是ios_base類的類級靜態常數,即其具有類作用域,所以如果在類的成員方法外面用它,則要用作用域運算子說明ios_base類。
#include <iostream>
int main()
{
using std::cout;
using std::ios_base;
float price1 = 20.40;
float price2 = 1.9 + 8.0/9.0;
cout.setf(ios_base::showpoint);//顯示小數點,且顯示末尾0
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
return 0;
}
20已經佔用了2位,所以小數點顯示了就不會再顯示後面的了。而且可以看到,小數點並沒有佔精度位。
"Furry Friends" is $20.4000!
"Fiery Friends" is $2.78889!
"Furry Friends" is $20.!
"Fiery Friends" is $2.8!
setf()是ios_base類的一個成員函數
ios_base類有一個受保護的數據成員,它的每一位,都表示和控制着輸出格式化的一個方面。比如有3個bit表示計數系統,hex,dec, oct。
除了上面展示的用控制符來改變輸出格式狀態外,還可以用setf()方法。
setf()有兩種過載版本。
第一種:
fmtflags setf(fmtflags);//用於設定單個位控制的格式資訊,返回的是設定之前的資訊
fmtflags是bitmask類的typedef名稱,用來儲存格式標記,在ios_base類中定義。bitmask類的關鍵思想是每一位都可以單獨存取,且都有自己的含義。iostream包使用bitmask來儲存狀態資訊。bitmask型別可以是整型,列舉或者STL的bitset容器。
fmtflags常數已經被ios_base類定義了, 我們不需要記住數位,直接傳入這些常數就好啦,但是要知道他們實際上就是bitmask型別的值哦,即fmtflags的值。
#include <iostream>
int main()
{
using std::cout;
using std::endl;
using std::ios_base;
int temperature = 63;
cout << "Today's water temperature:";
cout.setf(ios_base::showpos);//在十進制正數前面顯示+符號
cout << temperature << endl;
cout << "For our programming friends, that's ";
cout << std::hex << temperature << endl;
cout.setf(ios_base::uppercase);//對16禁進位制輸出,用大寫字母(只針對16進位制)
cout.setf(ios_base::showbase);//顯示基數字首0x
cout << "or\n";
cout << temperature << endl;
cout << "How " << true << "! oops -- How ";
cout.setf(ios_base::boolalpha);//顯示true或者false
cout << true << "!\n";
return 0;
}
Today's water temperature:+63
For our programming friends, that's
3f
or
0X3F
How 0X1! oops -- How true!
學到的點:
fmtflags setf(fmtflags, fmtflags);//返回的也是設定之前的資訊
第一個參數用來表示要設定哪些位(比如floatfield則設定科學位和定點位這兩個位元,其他位置完全不影響),和第一個原型的那個參數的功能一樣;第二個參數用來表示要清除哪些位,比如你要把進位制從十進制改爲16進位制,則要把hex那一位改爲1,還要把dec那一位改爲0呀。
而且由於ios_base類已經定義好了常數,就特別方便。
左對齊:把值放在欄位的左端。
內部對齊:把符號或者基數字首放在欄位左端,而數位放在欄位右邊。
範例
#include <iostream>
#include <cmath>
int main()
{
using std::cout;
using std::endl;
using std::ios_base;
cout.setf(ios_base::left, ios_base::adjustfield);//左對齊left justification
cout.setf(ios_base::showpos);//顯示正數前面的符號
cout.setf(ios_base::showpoint);//顯示小數點
cout.precision(3);
//使用科學模式
ios_base::fmtflags old = cout.setf(ios_base::scientific, ios_base::floatfield);//存住舊的儲存格式
long n;
cout << "左對齊\n";
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << " \n";
}
cout.setf(ios_base::internal, ios_base::adjustfield);
cout.setf(old, ios_base::floatfield);
cout << "內部對齊\n";
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
cout << "右對齊\n";
cout.setf(ios_base::right, ios_base::adjustfield);
cout.setf(ios_base::fixed, ios_base::floatfield);
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
return 0;
}
左對齊
+1 |+1.000e+000
+11 |+3.317e+000
+21 |+4.583e+000
+31 |+5.568e+000
+41 |+6.403e+000
內部對齊
+ 1|+ 1.00|
+ 11|+ 3.32|
+ 21|+ 4.58|
+ 31|+ 5.57|
+ 41|+ 6.40|
右對齊
+1| +1.000|
+11| +3.317|
+21| +4.583|
+31| +5.568|
+41| +6.403|
學到的點:
可以用下面 下麪這句程式碼設定爲預設模式,注意要強制轉換,不然會報錯,說不能把int轉換爲fmtflags型別:
cout.setf((ios_base::fmtflags)0, ios_base::floatfield);//第一個參數表示不設定任何位(科學位和定點位都不設定)
#include <iostream>
#include <cmath>
int main()
{
using std::cout;
using std::endl;
using std::ios_base;
cout.setf(ios_base::left, ios_base::adjustfield);//左對齊left justification
cout.setf(ios_base::showpos);//顯示正數前面的符號
cout.setf(ios_base::showpoint);//顯示小數點
cout.precision(3);
//使用科學模式
ios_base::fmtflags old = cout.setf(ios_base::scientific, ios_base::floatfield);//存住舊的儲存格式
long n;
cout << "左對齊(科學模式)\n";
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << " \n";
}
cout.setf(ios_base::internal, ios_base::adjustfield);
cout.setf(old, ios_base::floatfield);
cout << "內部對齊(預設模式)\n";
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
cout << "右對齊(定點模式)\n";
cout.setf(ios_base::right, ios_base::adjustfield);
cout.setf(ios_base::fixed, ios_base::floatfield);
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
//使用預設模式
cout << "右對齊(預設模式)\n";
cout.setf((ios_base::fmtflags)0, ios_base::floatfield);
for (n=1;n<=41;n+=10){
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
return 0;
}
左對齊(科學模式)
+1 |+1.000e+000
+11 |+3.317e+000
+21 |+4.583e+000
+31 |+5.568e+000
+41 |+6.403e+000
內部對齊(預設模式)
+ 1|+ 1.00|
+ 11|+ 3.32|
+ 21|+ 4.58|
+ 31|+ 5.57|
+ 41|+ 6.40|
右對齊(定點模式)
+1| +1.000|
+11| +3.317|
+21| +4.583|
+31| +5.568|
+41| +6.403|
右對齊(預設模式)
+1| +1.00|
+11| +3.32|
+21| +4.58|
+31| +5.57|
+41| +6.40|
範例:
#include <iostream>
int main()
{
using std::cout;
using std::ios_base;
float price1 = 20.40;
float price2 = 1.9 + 8.0/9.0;
cout.setf(ios_base::showpoint);//顯示小數點,且顯示末尾0
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
cout.unsetf(ios_base::showpoint);
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Friends\" is $" << price2 << "!\n";
return 0;
}
"Furry Friends" is $20.4000!
"Fiery Friends" is $2.78889!
"Furry Friends" is $20.!
"Fiery Friends" is $2.8!
"Furry Friends" is $20!
"Fiery Friends" is $2.8!
另一個範例:
#include <iostream>
int main()
{
using std::cout;
using std::endl;
using std::ios_base;
int temperature = 63;
cout << "Today's water temperature:";
cout.setf(ios_base::showpos);//在十進制正數前面顯示+符號
cout << temperature << endl;
cout << "For our programming friends, that's ";
cout << std::hex << temperature << endl;
cout.setf(ios_base::uppercase);//對16禁進位制輸出,用大寫字母(只針對16進位制)
cout.setf(ios_base::showbase);//顯示基數字首0x
cout << "or\n";
cout << temperature << endl;
cout << "How " << true << "! oops -- How ";
cout.setf(ios_base::boolalpha);//顯示true或者false
cout << true << "!\n";
cout.unsetf(ios_base::boolalpha);//不顯示true或者false
cout << true << "!\n";
return 0;
}
Today's water temperature:+63
For our programming friends, that's 3f
or
0X3F
How 0X1! oops -- How true!
0X1!
unsetf(ios_base::field);
都會切換回預設模式。setf()成員方法可以設定格式,但是需要提供各種常數參數,不是很方便,C++爲了使用者控制格式更方便,就提供了很多控制符。比如dec, hex, oct
cout << dec << left << fixed;//使用十進制計數,顯示左對齊,定點模式輸出浮點數
其他的控制符還有:
這張表的右邊是用setf()或者unsetf()方法,可以看到使用控制符真的方便很多,打字都少了好多。
這都要歸功於過載的插入運算子哇。
io manipulator 輸入輸出控制符
前面的標準控制符不能設定欄位寬度,所以iomanip標頭檔案又加了一些控制符,但是這些控制符和標準控制符不一樣,=他們帶參數,但是他們不是成員函數,仍然是控制符,所以還是用插入運算子使用它們!!
setprecision()
:設定精度,接受一個指定精度的整數參數setfill()
:接受一個指定填充字元的char參數setw()
:接受一個指定欄位寬度的整數參數#include <iostream>
#include <iomanip>
#include <cmath>
int main(){
using namespace std;
cout << fixed << right;
cout << setw(6) << "N" << setw(14) << "square root"
<< setw(15) << "fourth root\n";
double root;
for (int n=10;n<=100;n+=10){
root = sqrt(double(n));
cout << setw(6) << setfill('.') << n << setfill(' ')
<< setw(12) << setprecision(3) << root
<< setw(14) << setprecision(4) << sqrt(root)
<< endl;
}
return 0;
}
N square root fourth root
....10 3.162 1.7783
....20 4.472 2.1147
....30 5.477 2.3403
....40 6.325 2.5149
....50 7.071 2.6591
....60 7.746 2.7832
....70 8.367 2.8925
....80 8.944 2.9907
....90 9.487 3.0801
...100 10.000 3.1623
cout << 1.25
就會呼叫ostream & operator<<(double)
原型,而這個函數的功能就是把double浮點數轉換爲文字,在這裏就是轉換爲四個字元。