1. 程式人生 > >《C++面向物件程式設計》課程筆記 lessen7

《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;

}
  • 同一個類模板的兩個模板類是不相容的。完全不相關。