國慶沒回家,未來7天應該每天一篇筆記。
本來打算把複合類型看完,不過太長了,分兩天。
2011-10-01(Compound Types I)
1、字符串的拼接。任何由空白分隔符(空格、製表和換行符)分割的字符串常量都自動拼接成一個:
cout << "One Statement""Another Statement\n";
2、cin以空白符界定讀取範圍。如:
char front[20];
char tail[20];
cout << "Enter front: ";
cin >> front;
cout << "Enter tail: ";
cin >> tail;
cout << "Result: front = " << front << "; tail = " << tail << endl;
測試: Enter front: Marine King
Enter tail: Result: front = Marine; tail = King
3、get()和getline()。由於有了上面的問題,所以iostream類提供了兩個面向行的字符串輸入函數。當然,面向行亦有換行符的處理問題:getline()將'\n'視作讀取結束標誌,並將其拋棄;get()將'\n'視作讀取結束標誌,並留在輸入隊列中。
I、getline()。getline()是iostream的實例函數,cin是iostream的對象,所以要通過“.”來調用。它的參數有兩個,一個是字符數組,一個是你要接受的字符數,即可以用以下方式調用:
cin.getline(char arrayName[], int max);
II、get()。get()有幾個形式(即已被重載),其中一種形式與getline()一樣,即:
cin.get(char arrayName[], int max);
由於get()是將'\n'留在輸入隊列中的,所以如果使用get()的方法和getline()一樣,將出現問題,即(與2一樣的設定):
cin.get(front,20);
cin.get(tail,20);
運行,將會出現如下結果:
Enter front: Marine
Enter tail: Result: front = Marine; tail =
原因是,輸入Marine按下的'Enter'鍵是作爲'\n'留在輸入隊列,下一次調用get(),一開始就讀取到讀取結束標誌'\n',認爲已達行尾,程序直接執行下一行。解決的辦法是把'\n'從輸入隊列中去掉,可調用get()的另一種形式,這種形式的功能是字符讀取,將'\n'讀取:
cin.get(front,20);
cin.get();
cin.get(tail,20);
當然,cin.get(front,20)返回的是一個cin對象,所以也可以採用如下形式:
cin.get(front,20).get();
cin.get(tail,20);
順便說一句,getline()返回的也是cin的一個對象,所以……都懂的。
4、使用get()和getline()的程序健壯性問題。雖然使用get()在正常情況下代碼量多於使用getline(),但還是要建議多使用get(),因爲在處理輸入字符串長度過長問題上要優於getline()。
#include <iostream>
int main()
{
using namespace std;
char front[10];
char tail[10];
//char ch;
cout << "Enter front: ";
cin.getline(front,10);//.get(ch);
/*if(ch == '\n')
cout << "Chars less than 10" << "; The lost char: " << ch <<endl;
else
cout << "Chars more than 10" << "; The lost char: " << ch <<endl;*/
cout << "Enter tail: ";
cin.getline(tail,10);
cout << "Result: front = " << front << "; tail = " << tail << endl;
return 0;
}
如果第一次輸入的是:1234567890123(長度大於等於10),則出現以下結果:Enter front: 1234567890123
Enter tail: Result: front = 123456789; tail =
即當字符串長度大於等於所設定的最大長度值,截去數組可儲存的部分後,餘下的並不會被下一個getline()所讀取。
將上述程序getline()函數改爲get(),並去掉註釋部分,運行三次分別輸入:123456,654321(length<10);1234567890(length=10);1234567890123(length>10):
第一次結果:
Enter front: 123456
You enter chars less than 10; The lost char:
Enter tail: 654321
Result: front = 123456; tail = 654321
第二次結果:
Enter front: 1234567890
Chars more than 10; The lost char: 0
Enter tail: Result: front = 123456789; tail =
第三次結果:
Enter front: 1234567890123
Chars more than 10; The lost char: 0
Enter tail: Result: front = 123456789; tail = 123
可以看出,get()的優勢在於,將包括換行符在內的所有字符都留在輸入隊列,通過對get()參數的控制和'\n'的取捨任意地讀取所需的字符串。
5、string類。C++提供了另一個C中沒有的字符串處理方式:使用string類。string是位於名稱空間std中的,所以需使用using指令,另外,如果要使用string類,頭文件應包括#include <string>。
對string對象的處理方法是取之於C語言而高於C語言:
string str1; //定義長度爲0的string對象
string str2 = "abc"; //定義並初始化
str1 = str2; //賦值
string str3 = str1 + str2; //拼接
cout << str2[1] << endl; //string對象具有字符數組性質,輸出'b'
cout << str3 << endl; //輸出abcabc
通過“+”和“=”而不是C語言中的strcat()和strcpy(),不但書寫簡便,而且不會有字符數超出數組最大長度的情況。6、string對象的輸入。輸出使用cout這個不必提。輸入因爲cin是以空白符爲讀取結束標誌,使用它有諸多不便,所以要使用:
string str;
getline(cin,str);
來進行整行的字符輸入,cin作爲一個參數,標明要到哪裏去找輸入。至於爲什麼getline()有時需要cin調用(表明是istream的類方法),有時不需要(表明不是類方法),涉及到友元函數,教材沒講清楚,這裏不便論述。
7、結構體。
①VC++7.1之後才能使用string類作爲結構體成員;
②結構體可定義在所有函數之外也可定義main函數內,應用範圍有所不同。當定義在main外的時候,且結構體成員包括string,則名稱空間也應聲明在main之外,否則應使用std::string。
③結構體初始化一般採用數組形式:
struct Student
{
char name[20];
int number;
};
Student stu1 =
{
"Tom",
25
};
注意到結構體成員之間的逗號。當然也可以寫成Student
stu1 = {"Tom",25};④結構體可同時完成聲明和創建變量工作:
struct Student
{
char name[20];
int number;
}stu1,stu2;
也可以同時完成初始化的工作:struct Student
{
//the same as above
}stu1 = {"Tom",25};
不過如此做可讀性差點。⑤結構體可無名稱,如struct{int x; int y;}position;以後通過position來訪問,但這樣後面就不能創建這種類型的結構體變量了。
⑥結構體可賦值,在成員變量有字符數組的情況下也可進行。如,在初始化完stu1而stu2沒有的情況下,可通過stu2=stu1賦值。
8、共同體。是一種數據格式,能儲存不同的數據類型,但只能同時儲存一種。就像一個學生,他/她可以用學號(int型)或者姓名(char[]型)代表。共同體的定義和應用可以如下:
#include <iostream>
#include <string>
using namespace std;
struct Student
{
int type;//if type = 0,ID's form is int,otherwise,is string
union ID
{
int number;
char name[20];
}ID_val;
};
int main()
{
Student stu[3] = { {1},{0},{1} };
for(int i = 0;i<3;i++)
{
cout << "Enter student " << i+1 << ": \n";
if(stu[i].type == 1)
{
cout << "Enter a number as his or her ID: ";
(cin >> stu[i].ID_val.number).get();
}
else
{
cout << "Enter a string as his or her ID: ";
cin.get(stu[i].ID_val.name,20).get();
}
}
for(int i = 0;i<3;i++)
if(stu[i].type == 1)cout << "Student " << i+1 <<"'s ID: " << stu[i].ID_val.number <<endl;
else cout << "Student " << i+1 <<"'s ID: " << stu[i].ID_val.name <<endl;
return 0;
}
輸入輸出如下:Enter student 1:
Enter a number as his or her ID: 25
Enter student 2:
Enter a string as his or her ID: Tom
Enter student 3:
Enter a number as his or her ID: 30
Student 1's ID: 25
Student 2's ID: Tom
Student 3's ID: 30
注意到17行,可部分初始化;注意到第24行,用get()來接收輸入數字後的換行符,以免影響後面的字符串輸入。另外,共同體大小是確定的,等於共同體定義的類型中長度最大的,如此可看出,共同體是不允許定義string類的對象的,因爲其大小不確定。
9、匿名共同體。對於上面的例子,可以省去共同體名稱及其變量:ID和ID_val。則在調用它的時候,可以直接調用它的成員:
stu[i].number //34行部分修改
stu[i].name //35行部分修改
10、枚舉。其實是多個const的組合,即定義多個符號常量。
①定義。用enum關鍵字定義:
enum Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
默認情況下,系統將整數賦給枚舉量,Monday = 0,Tuesday =2 ...所以,上面的定義等效於:
const int Monday = 0;
const int Tuesday = 1;
//... ...
可以顯式地設置枚舉量的值:
enum race {Zerg = 0, Terran = 5, Protoss = 10};
也可以部分設置:
enum race {Zerg, Terran = 5,Protoss};
這時,Zerg = 0,Protoss = 6。
②枚舉量與整型。枚舉量是整型,可被提升爲int類型,但int不能被自動轉換成枚舉類型:
int day1 = Thursday; //合法,Thursday自動轉換成3
Day day2 = 3 //非法,3不能自動轉換成Thursday,可通過強制轉換:Day day2 = (Day)3;
Day day3 = Monday + Friday;//非法,Monday和Friday自動轉換成0和4,所得4不能轉換成枚舉型day3