C++面向對象程序設計(七)——輸入輸出和模板

C++面向對象程序設計(七)——輸入輸出和模板


本文是中國大學MOOC,北京大學程序設計與算法C++面向對象程序設計第七週筆記。本課程學習的github倉庫歡迎Fork

與輸入輸出流操作相關的類

ios
istream
ostream
ifstream
iostream
ofstream
fstream

isteam用於輸入的流類,例如cin是該類的對象

ostream用於輸出的流類,例如cout

ifstream是用於從文件讀取數據的類

ofstream用於向文件寫入的類

iostream既能輸入又能輸出的類

fstream既能從文件讀取數據,又能向文件寫入數據

輸出重定向

#include<iostream>
using namespace std;
int main(){
int x,y;
cin >> x >> y;
freopen("test.txt","w",stdout);//輸出重定向到text.txt文件
if( y ==0 )
    	cerr << "error." <<endl;
else
    	cout<<x/y;	//輸出結果到test.txt
return 0;

輸入重定向

#include<iostream>
using namespace std;
int main(){
double f;
int n;
freopen("t.txt","r",stdin);	//從t.txt讀取數據
cin >> f >> n;
cout << f << "," << n << endl;
return 0;
}

判斷輸入流結束

int x;
while(cin >> x)
{
    ......
}

istream類的成員函數

istream & getlinnen( char * buf, int bufSize);

從輸入流讀取bufSize-1個字符到緩衝區buf,或讀到碰到\n爲止

istream & getlinnen( char * buf, int bufSize,char delim);

從輸入流讀取bufSize-1個字符到緩衝區buf,或讀到碰到delim字符爲止

可以使用if(!cin.getline(...))判斷輸入是否結束

bool eof();判斷輸入流是否結束

int peek();返回下一個字符,但不從流中去掉

istream & putback( char c);將字符ch放回輸入流

istream & ignore(int nCout = 1, int delim = EOF);從流中刪掉最多nCout個字符,遇到EOF時結束

流操縱算子

需要#include<iomanip>

整數流的基數

流操縱算子。十進制dec,八進制oct,十六進制hex,setbase

浮點數的精度

precision是成員函數,調用方式爲

cout.precision(5);

setprecision是流操作算子,調用方式爲

cout << setprecision(5);	//可以連續輸出

定點輸出浮點數:

cout << setiosflags(ios::fixed) << setprecision(6) << endl;

設置域寬

setw成員函數

cin >> setw(4)
cout << setw(4)

width流操縱算子

cin.width(5);
cout.width(5);

寬度不足用空格在左邊補上

#include <iomanip>
using namespace std;
int main()
{
    int n =141;
    //十六進制,十進制,八進制先後輸出
    cout << hex << n << " " << dec << n << " " << oct << n << endl;
    double x =1234567.89, y =12.34567;
     //保留5位有效數字
    cout << setprecision(5) << x << " " << y << " " << endl;
    //保留小數點後5位
    cout << fixed << setprecision(5) << x << " " << y << endl;
    //科學計數法輸出,且保留小數點後5位
    cout << scientific << setprecision(5) << x << " " << y << endl;
    //非負數顯示正號,輸出寬度12字符,寬度不足使用'*'填補
    cout << showpos << fixed << setw(12) << setfill('*') << n << endl;
       //非負數不顯示正號,輸出寬度12字符,寬度不足使用'*'填補
    cout << noshowpos << fixed << setw(12) << setfill('*') << n << endl;
    //輸出寬度12字符,寬度不足左邊用填充字符填充
    cout << setw(12) << right << n << endl;
    //寬度不足時,負號和數值分列左右,中間用填充字符填充
    cout << setw(12)<< internal << n<< endl;
    
    return 0;
}

用戶自定義流操作算子

ostream & tab(ostream & output){
    return output << '\t';
}

cout << "aa" << tab << "bb" << endl;

文件讀寫

創建文件

#include <fstream>
ofstream outFile("clients.dat",ios::out|ios::binary);
//clients.dat 要創建的文件的名字
//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;
}

文件名可以給出絕對路徑,也可以給相對路徑。默認當前文件夾找文件

文件的讀寫指針

對於輸入文件,有一個讀指針

對於輸出文件,有一個寫指針

對於輸入輸出文件,有一個讀寫指針

標識文件操作的當前位置,該指針在哪裏,讀寫操作就在哪裏進行

ofstream fout("a1.out",ios::app);	//以添加方式打開
long location = fout.tellp();	//取得寫指針的位置
location = 10;	
fout.seekp(location);	//將寫指針移動到第10個字節處
fout.seekp(location,ios::beg);	//從頭數location
fout.seekp(location,ios::cur); //從當前位置數location
fout.seekp(location,ios::end);//從尾部數location
ifstream fin("a1.in",ios::ate);	//打開文件,定位文件指針到文件尾
long location = fin.tellg();	//取得指針的位置
location = 10L;	
fin.seekg(location);	//將讀指針移動到第10個字節處
fin.seekg(location,ios::beg);	//從頭數location
fin.seekg(location,ios::cur); //從當前位置數location
fin.seekg(location,ios::end);//從尾部數location

location可以爲負值

字符文件讀寫

#include<iostream>
#include<fstream>
#include<vector>
#include<alogrithm>

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)
        v.push_back(x);
sort(v.begin(),v.end());
for(int i = 0 ;i < v.size();i++)
        destFile << v[i] << " ";
destFile.close();
srcFile.close();
return 0;
}

二進制文件讀寫

二進制讀文件

istream & read (char * s, long n);
/*將文件讀指針指向的地方的n個字節的內容,讀入到內存地址s,然後將文件讀指針向後移動n字節*/

二進制寫文件

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 ));
    fout.close();
    ifstream fin("some.dat",ios::in|ios::binary);
    int y;
    fin.read((char * ) &y,sizeof(int));
    fin.close();
    cout << y << endl;
    return 0;
}
#include<iostream>
#include<fstream>

using namespace std;

struct Student{
    char name[20];
    int score;
};
//輸入
int main()
{
    Student s;
    ofstream OutFile("c:\\tmp\\students.dat",ios::out|ios::binary);
    while(cin >> s.name >> s.score)
        OutFile.write((char *) & s,sizeof(s));
    OutFile.close();
    return 0;
}

//讀出
int main()
{
	Student s;
    ifstream inFile("c:\\tmp\\students.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();
    return 0;
}
//修改

{
	Student s;
    fstream iofile("c:\\tmp\\students.dat",ios::in|ios::out|ios::binary);
    if(!ioFile){
        cout << "error" << endl;
        return 0;
    }
	iofile.seekp(2*sizeof(s),ios::beg);
    iofile.write("Mike",strlen("Mike")+1);
    iofile.seekg(0,ios::beg);
    while(iofile.read((char *)&s,sizeof(s)))
        cout << s.name <<" "<< s.score <<endl;
    iofile.close();
    return 0;
}

函數模板

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(duble &,double & )函數
    return 0; 

函數模板中可以有不止一個參數

template <class T1,class T2>
T2 print(T1 arg1,T2 arg2)
{
    cout << arg1 << " " << arg2 << endl;
    return arg2;
}

函數模板可以重載,只要形參表或類型參數表不同即可

多個函數和函數模板名字相同的情況下,編譯器處理順序

​ 1.先找參數完全匹配的普通函數

​ 2.再找參數完全匹配的模板函數

​ 3.實參經過自動類型轉換後能夠匹配的普通函數

匹配模板函數時,不進行類型自動轉換

//函數模板實例Map
#include<iostream>
using namespace std;
template<class T,class Pred>
void Map(T s, T e, T x,Pred op)
{
    for(; s!=e;++s,++x){
        *x = op(*s);
    }
}
int Cube(int x){
    return x*x*x;
}
double Square(double x){
    return x*x;
}

類模板

//類模板定義
template<typename 類型參數1, typename 類型參數2,……>
class 類模板名
{
			成員函數和成員變量
};
//類模板成員函數寫法:
template<class 類型參數1, class 類型參數2,……>
返回值類型 類模板名<類型參數名列表>::成員函數名 (參數表)
{
			函數體
};
//用類模板定義對象的寫法:
類模板名<真實類型參數表>對象名(構造函數實參表);
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
{
    return key<p.key;
}

int main(){
    Pair<string,int>student("Tom",19);
    cout << student.key << " " <<student.value;
    return 0;
}

同一個類模板的兩個模板類是不兼容的

## 類模板和派生

  1. 類模板從類模板派生
  2. 類模板從模板類派生
  3. 類模板從普通類派生
  4. 普通類從模板類派生

類模板和友元

函數,類,類的成員函數作爲類模板的友元

函數模板作爲類模板的友元

函數模板作爲類的友元

類模板作爲類模板的友元

類模板與static成員

#include<iostream>

using namespace std;
template <class T>
class A
{
    private:
    	static int count;
    public:
    A(){count++;}
    ~A(){count--;}
    A(A&){count ++ ;}
    static void PrintCount(){cout << count <<endl;}
};
template<> int A<int> :: count = 0;
template<>int A<double>::count = 0;
int main(){
    A<int> ia;
    A<double> da;
    ia.PrintCount();
    da.PrintCount();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章