C++面向對象程序設計(七)——輸入輸出和模板
文章目錄
本文是中國大學MOOC,北京大學程序設計與算法C++面向對象程序設計第七週筆記。本課程學習的github倉庫歡迎Fork
與輸入輸出流操作相關的類
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;
}
同一個類模板的兩個模板類是不兼容的
## 類模板和派生
- 類模板從類模板派生
- 類模板從模板類派生
- 類模板從普通類派生
- 普通類從模板類派生
類模板和友元
函數,類,類的成員函數作爲類模板的友元
函數模板作爲類模板的友元
函數模板作爲類的友元
類模板作爲類模板的友元
類模板與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;
}