《C++面向物件程式設計》課程筆記 lessen7
1. 輸入輸出流相關的類
- istream 是用於輸入的流類, cin 就是該類的物件。
- ostream 是用於輸出的流類, cout 就是該類的物件。
- ifstream 是用於從檔案讀取資料的類。
- ofstream 是用於向檔案寫入資料的類。
- iostream 是既能用於輸入,又能用於輸出的類。
- fstream 是既能從檔案讀取資料,又能向檔案寫入資料的類。
- 輸入流物件:cin 與標準輸入裝置相連,對應於標準輸入流。用於從鍵盤讀取資料,也可以被重定向為從檔案中讀取資料。
- 輸出流物件:cout 與標準輸出裝置相連,對應於標準輸出流。用於向螢幕輸出資料,也可以被重定向為向檔案寫入資料。
cerr 與標準錯誤輸出裝置相連,對應於標準錯誤輸出流,用於向螢幕輸出出錯資訊。
clog 與標準錯誤輸出裝置相連 ,對應於標準錯誤輸出流,用於向螢幕輸出出錯資訊。
預設情況下:(三者一樣)
cerr << "Hello World!" << endl;
clog << "Hello World!" << endl;
cout << "Hello World!" << endl;
輸出流重定向:
#include <iostream> using namespace std; int main() { int x,y; cin >> x >> y; freopen("test.txt","w",stdout); //將標準輸出重定向到 test.txt 檔案 if( y==0 ) //除數為0則在螢幕上輸出錯誤資訊 cerr << "errer" <<endl; else cout << x/y; //輸出結果到 test.txt system("pause"); return 0; }
輸入流重定向:
int main()
{
double f;
int n;
freopen("t.txt","r",stdin); //cin 被改為從 t.txt 中讀取資料
cin >> f >>n;
cout << f << "," << n << endl;
system("pause");
return 0;
}
istream 類的成員函式:
istream & getline(char * buf, int bufsize);
該函式從輸入流中讀取 bufsize-1 個字元到緩衝區 buf ,或讀到 ‘\n’ 為止(哪個先到算哪個)。
istream & getline(char * buf, int bufsize, char delim);
該函式從輸入流中讀取 bufsize-1 個字元到緩衝區 buf ,或讀到 delim 字元為止(哪個先到算哪個)。
兩個函式都會自動在 buf 中讀入資料的結尾新增 '\0' 。'\n' 或 delim 都不會被讀入 buf ,但會被從輸入流中取走。
可以用 if( !cin.getline(...) ) 判斷輸入是否結束。
#include <iostream>
using namespace std;
int main()
{
int x;
char buf[100];
cin >> x;
cin.getline(buf,90);
cout << buf <<endl;
system("pause");
return 0;
}
2. 流操縱運算元
- 整數流的基數:流操縱運算元 dec(十進位制), oct(八進位制), hex(十六進位制), setbase(任意進位制)
- 浮點數的精度(precision, setprecision)
- 設定域寬(setw, width)
- 使用者自定義的流操縱運算元
使用流操縱運算元需要 #include <iomanip>
1 整數流的基數
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int n = 10;
cout << n << endl;
cout << hex << n <<endl; //十六進位制
cout << dec << n <<endl; //十進位制
cout << oct << n <<endl; //八進位制
system("pause");
return 0;
}
2 控制浮點數精度的流操縱運算元
- precision 是成員函式,其呼叫方式為:
cout.precision(5);
- setprecision 是流操作運算元,其呼叫方式為:
cout << setprecision(5); //可以連續輸出
它們的功能相同。指定輸出浮點數的有效位數(非定點方式輸出時)。指定輸出浮點數小數點後的有效位數(定點方式輸出時)。定點方式:小數點必須出現在個位數後面。
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double x = 1234567.89,y = 12.34567;
int n = 1234567;
int m = 12;
cout << setprecision(6) << x <<endl << y << endl << n << endl << m;
//浮點數輸出最多6位有效數字(預設情況下為非定點方式輸出)
system("pause");
return 0;
}
//輸出: 1.23457e+006
12.3457
1234567 (整數不受精度影響)
12
設定和取消以小數點固定位置方式(定點方式)輸出的程式碼:
cout << setiosflags(ios::fixed);
cout << resetiosflags(ios::fixed);
cout << setiosflags(ios::fixed) << setprecision(6) << x <<endl << y << endl << n << endl << m;
//輸出 :1234567.890000
12.345670
1234567
12
3 設定域寬的流操縱運算元
setw 是流操作運算元,width 是成員函式。
cin >> setw(4);
cin.width(5);
cout << setw(4);
cout.width(5);
寬度設定有效性是一次性的,在每次讀入和輸出之前都要設定寬度。
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int w = 4;
char string[10];
cin.width(5); //讀入域寬時包含 '\0'
while(cin>>string)
{
cout.width(w++); //輸出時域寬不包含 '\0'。字元不夠時在前面補空格
cout<<string<<endl;
cin.width(5);
}
system("pause");
return 0;
}
//輸出:1234567890
1234
5678 //字元不夠時在前面補空格
90
3. 檔案讀寫
1 建立檔案
#include <fstream> //包含標頭檔案
ofstream outFile("clients.dat",ios::out|ios::binary);
- "clients.dat" 要建立的檔案的名字
- ios::out 檔案開啟方式
- ios::out 輸出到檔案,刪除原因內容
- ios::app 輸出到檔案,保留原有內容,總是在尾部新增
- ios::binary 以二進位制檔案格式開啟檔案
也可以先建立 ofstream 物件,再用 open 函式開啟:
ofstream fout;
fout.open("test.out",ios::out|ios::binary);
判斷開啟是否成功:
if (!fout)
{
cout << "File open error!" << endl;
}
檔名可以給出絕對路徑,也可以給相對路徑。沒有交代路徑資訊,就是在當前資料夾下找檔案。
2 字元檔案讀寫
寫一個程式,將檔案 in.txt 裡面的整數排序後,輸出到 out.txt
例如:若 in.txt 的內容為:1 234 9 45 6 879
則執行本程式後。生成的 out.txt 的內容為:1 6 9 45 234 879
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v;
ifstream srcFile("in.txt",ios::in);
ofstream destFile("out.txt",ios::out);
int x;
while( srcFile >> x ) //ifstream 是 istream 的派生類,istream 有的操作 ifstream 也有
v.push_back(x);
sort(v.begin(),v.end());
for (int i=0;i<v.size();i++)
{
destFile << v[i] << " "; //ofstream 是 ostream 的派生類,ostream 有的操作 ofstream 也有
}
destFile.close();
srcFile.close();
system("pause");
return 0;
}
3 二進位制檔案讀寫
- 二進位制讀檔案
ifstream 和 fstream 的成員函式:
istream & read(char *s, long n);
將檔案讀指標指向的地方的 n 個位元組內容,讀入到記憶體地址 s ,然後將檔案讀指標向後移動 n 位元組(以 ios::in 方式開啟檔案時,一開始檔案讀指標指向檔案開頭)。
- 二進位制寫檔案
istream & write(const char* s, long n);
將記憶體地址 s 處的 n 個位元組內容,寫入到檔案中寫指標指向的位置,然後將檔案寫指標向後移動 n 位元組(以 ios::out 方式開啟檔案時,檔案寫指標一開始指向檔案開頭,以 ios::app 方式開啟檔案時,檔案寫指標一開始指向檔案尾部)。
在檔案中寫入和讀取一個整數例程:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream fout("some.dat",ios::out|ios::binary);
int x = 120;
fout.write((const char *)(&x),sizeof(int)); //x 的記憶體地址為 &x,是 int* 型別,而引數型別需為 const char* 型別。
fout.close();
ifstream fin("some.dat",ios::in|ios::binary);
int y;
fin.read((char *)(&y),sizeof(int));
fin.close();
cout << y <<endl;
system("pause");
return 0;
}
從鍵盤輸入幾個學生的姓名和成績,並以二進位制檔案形式儲存。
#include <iostream>
#include <fstream>
using namespace std;
struct Student
{
char name[20];
int score;
};
int main()
{
Student s;
ofstream OutFile("student.dat",ios::out|ios::binary);
while(cin >> s.name >> s.score)
OutFile.write((char *)(&s),sizeof(s)); //每個 s 佔24位元組
OutFile.close();
system("pause");
return 0;
}
將 student.dat 檔案內容讀出並顯示:
struct Student
{
char name[20];
int score;
};
int main()
{
Student s;
ifstream inFile("student.dat",ios::in|ios::binary);
if (!inFile)
{
cout << "error" <<endl;
return 0;
}
while(inFile.read((char *)(&s),sizeof(s)))
{
int readedBytes = inFile.gcount(); //看剛才讀了多少位元組
cout << s.name << " " << s.score << endl;
}
inFile.close();
system("pause");
return 0;
}
4. 函式模板
1 函式模板的格式
template <class 型別引數1,class 型別引數2,...>
返回值型別 模板名(形參表)
{
函式體
}
template <class T>
void Swap(T & x,T & y)
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int n = 1,m = 2;
Swap(n , m); //編譯器自動生成 void Swap(int &,int &)函式
double f = 1.2,g = 2.3;
Swap(f,g); //編譯器自動生成 void Swap(double &,double &)函式
// Swap(n,f); //error.匹配模板函式時,不進行型別自動轉換
system("pause");
return 0;
}
- 函式模板中可以有不止一個型別引數。
- 函式模板可以過載,只要它們的形參表或型別引數表不同即可。
- 匹配模板函式時,不進行型別自動轉換.
函式模板例項:Map
//函式模板例項:Map
template<class T,class Pred>
void Map(T s,T e,T x,Pred op) //s 起點位置,e 終點位置,op 變換,然後複製到起點為 x 的地方
{
for(; s != e; ++s,++x)
{
*x = op(*s);
}
}
int Cube(int x)
{
return x * x * x;
}
double Square(double x)
{
return x * x;
}
int a[5] = {1,2,3,4,5},b[5];
double d[5] = {1.1,2.1,3.1,4.1,5.1}, c[5];
int main()
{
Map(a,a+5,b,Square); //void Map(int * s,int * e,int * x,double (*op)(double)) //第四個引數是函式指標型別(返回值double,引數double)
for(int i=0;i<5;++i)
cout << b[i] <<",";
cout << endl;
Map(a,a+5,b,Cube);
for(int i=0;i<5;i++)
cout << b[i] << ",";
cout << endl;
Map(d,d+5,c,Square);
for(int i=0;i<5;i++)
cout << c[i] << ",";
cout << endl;
system("pause");
return 0;
}
5. 類模板
template <class 型別引數1,class 型別引數2,...>
(也可以)template <typename 型別引數1,typename 型別引數2,...>
//型別引數表
class 類模板名
{
成員函式和成員變數
};
類模板裡成員函式的寫法:
template <class 型別引數1,class 型別引數2,...> //型別引數表
返回值型別 類模板名<型別引數名列表>::成員函式名 (引數表)
{
......
}
用類模板定義物件的寫法:
類模板名 <真實型別引數表> 物件名(建構函式實參表);
類模板例項:Pair類模板
//類模板例項:Pair類模板
template <class T1,class T2>
class Pair
{
public:
T1 key; //關鍵字
T2 value; //值
Pair(T1 k,T2 v):key(k),value(v) { };
bool operator < (const Pair<T1,T2> & p) const;
};
template<class T1,class T2>
bool Pair<T1,T2>::operator<(const Pair<T1,T2> & p) const
{//Pair 的成員函式 operator <
return key < p.key;
}
int main()
{
Pair<string,int> student("Tom",19);
//例項化出一個類 Pair<string,int>
cout << student.key << " " << student.value;
system("pause");
return 0;
}
- 同一個類模板的兩個模板類是不相容的。完全不相關。