C++学习 高级编程
C++?文件和流
- 到目前為止,目前使用最為廣泛的是?iostream?標(biāo)準(zhǔn)庫,它提供了?cin?和?cout?方法分別用于從標(biāo)準(zhǔn)輸入讀取流和向標(biāo)準(zhǔn)輸出寫入流。
- 以下將介紹從文件讀取流和向文件寫入流。這就需要用到 C++ 中另一個標(biāo)準(zhǔn)庫?fstream,它定義了三個新的數(shù)據(jù)類型:
| ofstream | 該數(shù)據(jù)類型表示輸出文件流,用于創(chuàng)建文件并向文件寫入信息。 |
| ifstream | 該數(shù)據(jù)類型表示輸入文件流,用于從文件讀取信息。 |
| fstream | 該數(shù)據(jù)類型通常表示文件流,且同時具有 ofstream 和 ifstream 兩種功能,這意味著它可以創(chuàng)建文件,向文件寫入信息,從文件讀取信息。 |
- 源代碼文件中包含頭文件 <iostream> 和 <fstream>
打開文件
- 在從文件讀取信息或者向文件寫入信息之前,必須先打開文件。ofstream?和?fstream?對象都可以用來打開文件進(jìn)行寫操作,如果只需要打開文件進(jìn)行讀操作,則使用?ifstream?對象。
- 下面是 open() 函數(shù)的標(biāo)準(zhǔn)語法,open() 函數(shù)是 fstream、ifstream 和 ofstream 對象的一個成員。
- 在這里,open()?成員函數(shù)的第一參數(shù)指定要打開的文件的名稱和位置,第二個參數(shù)定義文件被打開的模式。?
| ios::app | 追加模式。所有寫入都追加到文件末尾。 |
| ios::ate | 文件打開后定位到文件末尾。 |
| ios::in | 打開文件用于讀取。 |
| ios::out | 打開文件用于寫入。 |
| ios::trunc | 如果該文件已經(jīng)存在,其內(nèi)容將在打開文件之前被截斷,即把文件長度設(shè)為 0。 |
- 可以把以上兩種或兩種以上的模式結(jié)合使用。例如,如果想要以寫入模式打開文件,并希望截斷文件,以防文件已存在,那么可以使用下面的語法:
- 類似地,如果想要打開一個文件用于讀寫,可以使用下面的語法:
關(guān)閉文件
- 當(dāng) C++ 程序終止時,它會自動關(guān)閉刷新所有流,釋放所有分配的內(nèi)存,并關(guān)閉所有打開的文件。但程序員應(yīng)該養(yǎng)成一個好習(xí)慣,在程序終止前關(guān)閉所有打開的文件。
- 下面是 close() 函數(shù)的標(biāo)準(zhǔn)語法,close() 函數(shù)是 fstream、ifstream 和 ofstream 對象的一個成員。
寫入文件
- 在 C++ 編程中,使用流插入運算符( << )向文件寫入信息,就像使用該運算符輸出信息到屏幕上一樣。唯一不同的是,在這里使用的是?ofstream?或?fstream?對象,而不是?cout?對象。
讀取文件
- 在 C++ 編程中,使用流提取運算符( >> )從文件讀取信息,就像使用該運算符從鍵盤輸入信息一樣。唯一不同的是,在這里使用的是?ifstream?或?fstream?對象,而不是?cin?對象。
讀取 & 寫入實例
- 下面的 C++ 程序以讀寫模式打開一個文件。在向文件 afile.dat 寫入用戶輸入的信息之后,程序從文件讀取信息,并將其輸出到屏幕上
- ?cin 對象的附加函數(shù),比如 getline()函數(shù)從外部讀取一行,ignore() 函數(shù)會忽略掉之前讀語句留下的多余字符
文件位置指針
- istream?和?ostream?都提供了用于重新定位文件位置指針的成員函數(shù)。這些成員函數(shù)包括關(guān)于 istream 的?seekg("seek get")和關(guān)于 ostream 的?seekp("seek put")。
- seekg 和 seekp 的參數(shù)通常是一個長整型。第二個參數(shù)可以用于指定查找方向。查找方向可以是?ios::beg(默認(rèn)的,從流的開頭開始定位),也可以是?ios::cur(從流的當(dāng)前位置開始定位),也可以是?ios::end(從流的末尾開始定位)。
- 文件位置指針是一個整數(shù)值,指定了從文件的起始位置到指針?biāo)谖恢玫淖止?jié)數(shù)。下面是關(guān)于定位 "get" 文件位置指針的實例:
C++?異常處理
異常是程序在執(zhí)行期間產(chǎn)生的問題。C++ 異常是指在程序運行時發(fā)生的特殊情況,比如嘗試除以零的操作。異常提供了一種轉(zhuǎn)移程序控制權(quán)的方式。C++ 異常處理涉及到三個關(guān)鍵字:try、catch、throw。
- throw:?當(dāng)問題出現(xiàn)時,程序會拋出一個異常。這是通過使用?throw?關(guān)鍵字來完成的。
- catch:?在您想要處理問題的地方,通過異常處理程序捕獲異常。catch?關(guān)鍵字用于捕獲異常。
- try:?try?塊中的代碼標(biāo)識將被激活的特定異常。它后面通常跟著一個或多個 catch 塊。
如果有一個塊拋出一個異常,捕獲異常的方法會使用?try?和?catch?關(guān)鍵字。try 塊中放置可能拋出異常的代碼,try 塊中的代碼被稱為保護(hù)代碼。使用 try/catch 語句的語法如下所示:
try {// 保護(hù)代碼 }catch( ExceptionName e1 ) {// catch 塊 }catch( ExceptionName e2 ) {// catch 塊 }catch( ExceptionName eN ) {// catch 塊 }- 如果?try?塊在不同的情境下會拋出不同的異常,這個時候可以嘗試羅列多個?catch?語句,用于捕獲不同類型的異常。
拋出異常
- 可以使用?throw?語句在代碼塊中的任何地方拋出異常。throw 語句的操作數(shù)可以是任意的表達(dá)式,表達(dá)式的結(jié)果的類型決定了拋出的異常的類型。
捕獲異常
catch?塊跟在?try?塊后面,用于捕獲異常。您可以指定想要捕捉的異常類型,這是由 catch 關(guān)鍵字后的括號內(nèi)的異常聲明決定的。
try {// 保護(hù)代碼 }catch( ExceptionName e ) {// 處理 ExceptionName 異常的代碼 }- 上面的代碼會捕獲一個類型為?ExceptionName?的異常。如果您想讓 catch 塊能夠處理 try 塊拋出的任何類型的異常,則必須在異常聲明的括號內(nèi)使用省略號 ...,如下所示:
- 由于拋出了一個類型為?const char*?的異常,因此,當(dāng)捕獲該異常時,必須在 catch 塊中使用 const char*。當(dāng)上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
C++ 標(biāo)準(zhǔn)的異常
- C++ 提供了一系列標(biāo)準(zhǔn)的異常,定義在?<exception>?中,可以在程序中使用這些標(biāo)準(zhǔn)的異常。它們是以父子類層次結(jié)構(gòu)組織起來的,如下所示:
| std::exception | 該異常是所有標(biāo)準(zhǔn) C++ 異常的父類。 |
| std::bad_alloc | 該異常可以通過?new?拋出。 |
| std::bad_cast | 該異常可以通過?dynamic_cast?拋出。 |
| std::bad_exception | 這在處理 C++ 程序中無法預(yù)期的異常時非常有用。 |
| std::bad_typeid | 該異常可以通過?typeid?拋出。 |
| std::logic_error | 理論上可以通過讀取代碼來檢測到的異常。 |
| std::domain_error | 當(dāng)使用了一個無效的數(shù)學(xué)域時,會拋出該異常。 |
| std::invalid_argument | 當(dāng)使用了無效的參數(shù)時,會拋出該異常。 |
| std::length_error | 當(dāng)創(chuàng)建了太長的 std::string 時,會拋出該異常。 |
| std::out_of_range | 該異常可以通過方法拋出,例如 std::vector 和 std::bitset<>::operator[]()。 |
| std::runtime_error | 理論上不可以通過讀取代碼來檢測到的異常。 |
| std::overflow_error | 當(dāng)發(fā)生數(shù)學(xué)上溢時,會拋出該異常。 |
| std::range_error | 當(dāng)嘗試存儲超出范圍的值時,會拋出該異常。 |
| std::underflow_error | 當(dāng)發(fā)生數(shù)學(xué)下溢時,會拋出該異常。 |
定義新的異常
- 可以通過繼承和重載?exception?類來定義新的異常。下面的實例演示了如何使用 std::exception 類來實現(xiàn)自己的異常:
- what()?是異常類提供的一個公共方法,它已被所有子異常類重載。這將返回異常產(chǎn)生的原因。
C++?動態(tài)內(nèi)存
了解動態(tài)內(nèi)存在 C++ 中是如何工作的是成為一名合格的 C++ 程序員必不可少的。C++ 程序中的內(nèi)存分為兩個部分:
- 棧:在函數(shù)內(nèi)部聲明的所有變量都將占用棧內(nèi)存。
- 堆:這是程序中未使用的內(nèi)存,在程序運行時可用于動態(tài)分配內(nèi)存。
很多時候,無法提前預(yù)知需要多少內(nèi)存來存儲某個定義變量中的特定信息,所需內(nèi)存的大小需要在運行時才能確定。在 C++ 中,可以使用特殊的運算符為給定類型的變量在運行時分配堆內(nèi)的內(nèi)存,這會返回所分配的空間地址。這種運算符即?new?運算符。如果不再需要動態(tài)分配內(nèi)存空間,可以使用?delete?運算符,刪除之前由 new 運算符分配的內(nèi)存。
new 和 delete 運算符
下面是使用 new 運算符來為任意的數(shù)據(jù)類型動態(tài)分配內(nèi)存的通用語法:
new data-type;- 在這里,data-type?可以是包括數(shù)組在內(nèi)的任意內(nèi)置的數(shù)據(jù)類型,也可以是包括類或結(jié)構(gòu)在內(nèi)的用戶自定義的任何數(shù)據(jù)類型。如果使用內(nèi)置的數(shù)據(jù)類型,例如,可以定義一個指向 double 類型的指針,然后請求內(nèi)存,該內(nèi)存在執(zhí)行時被分配。我們可以按照下面的語句使用?new?運算符來完成這點:
- 如果自由存儲區(qū)已被用完,可能無法成功分配內(nèi)存。所以建議檢查 new 運算符是否返回 NULL 指針,并采取以下適當(dāng)?shù)牟僮?#xff1a;
- malloc()?函數(shù)在 C 語言中就出現(xiàn)了,在 C++ 中仍然存在,但建議盡量不要使用 malloc() 函數(shù)。new 與 malloc() 函數(shù)相比,其主要的優(yōu)點是,new 不只是分配了內(nèi)存,它還創(chuàng)建了對象。
- 在任何時候,當(dāng)某個已經(jīng)動態(tài)分配內(nèi)存的變量不再需要使用時,可以使用 delete 操作符釋放它所占用的內(nèi)存,如下所示:
數(shù)組的動態(tài)內(nèi)存分配
- 假設(shè)要為一個字符數(shù)組(一個有 20 個字符的字符串)分配內(nèi)存,可以使用上面實例中的語法來為數(shù)組動態(tài)地分配內(nèi)存,如下所示:
- 要刪除剛才創(chuàng)建的數(shù)組,語句如下:
下面是 new 操作符的通用語法,可以為多維數(shù)組分配內(nèi)存
一維數(shù)組
// 動態(tài)分配,數(shù)組長度為 m int *array=new int [m];//釋放內(nèi)存 delete [] array;二維數(shù)組
int **array // 假定數(shù)組第一維長度為 m, 第二維長度為 n // 動態(tài)分配空間 array = new int *[m]; for( int i=0; i<m; i++ ) {array[i] = new int [n] ; } //釋放 for( int i=0; i<m; i++ ) {delete [] array[i]; } delete [] array;二維數(shù)組例子
#include <iostream> using namespace std;int main() {int **p; int i,j; //p[4][8] //開始分配4行8列的二維數(shù)據(jù) p = new int *[4];for(i=0;i<4;i++){p[i]=new int [8];}for(i=0; i<4; i++){for(j=0; j<8; j++){p[i][j] = j*i;}} //打印數(shù)據(jù) for(i=0; i<4; i++){for(j=0; j<8; j++) { if(j==0) cout<<endl; cout<<p[i][j]<<"\t"; }} //開始釋放申請的堆 for(i=0; i<4; i++){delete [] p[i]; }delete [] p; return 0; }三維數(shù)組
int ***array; // 假定數(shù)組第一維為 m, 第二維為 n, 第三維為h // 動態(tài)分配空間 array = new int **[m]; for( int i=0; i<m; i++ ) {array[i] = new int *[n];for( int j=0; j<n; j++ ){array[i][j] = new int [h];} } //釋放 for( int i=0; i<m; i++ ) {for( int j=0; j<n; j++ ){delete[] array[i][j];}delete[] array[i]; } delete[] array;三維數(shù)組例子
?
#include <iostream> using namespace std;int main() { int i,j,k; // p[2][3][4]int ***p;p = new int **[2]; for(i=0; i<2; i++) { p[i]=new int *[3]; for(j=0; j<3; j++) p[i][j]=new int[4]; }//輸出 p[i][j][k] 三維數(shù)據(jù)for(i=0; i<2; i++) {for(j=0; j<3; j++) { for(k=0;k<4;k++){ p[i][j][k]=i+j+k;cout<<p[i][j][k]<<" ";}cout<<endl;}cout<<endl;}// 釋放內(nèi)存for(i=0; i<2; i++) {for(j=0; j<3; j++) { delete [] p[i][j]; } } for(i=0; i<2; i++) { delete [] p[i]; } delete [] p; return 0; }對象的動態(tài)內(nèi)存分配
- 對象與簡單的數(shù)據(jù)類型沒有什么不同
- 如果要為一個包含四個 Box 對象的數(shù)組分配內(nèi)存,構(gòu)造函數(shù)將被調(diào)用 4 次,同樣地,當(dāng)刪除這些對象時,析構(gòu)函數(shù)也將被調(diào)用相同的次數(shù)(4次)。
- 當(dāng)上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
C++?命名空間
- 假設(shè)這樣一種情況,當(dāng)一個班上有兩個名叫 Zara 的學(xué)生時,為了明確區(qū)分它們,我們在使用名字之外,不得不使用一些額外的信息,比如他們的家庭住址,或者他們父母的名字等等。
- 同樣的情況也出現(xiàn)在 C++ 應(yīng)用程序中。例如,您可能會寫一個名為 xyz() 的函數(shù),在另一個可用的庫中也存在一個相同的函數(shù) xyz()。這樣,編譯器就無法判斷您所使用的是哪一個 xyz() 函數(shù)。
- 因此,引入了命名空間這個概念,專門用于解決上面的問題,它可作為附加信息來區(qū)分不同庫中相同名稱的函數(shù)、類、變量等。使用了命名空間即定義了上下文。本質(zhì)上,命名空間就是定義了一個范圍。
- 以一個計算機(jī)系統(tǒng)中的例子,一個文件夾(目錄)中可以包含多個文件夾,每個文件夾中不能有相同的文件名,但不同文件夾中的文件可以重名。
定義命名空間
- 命名空間的定義使用關(guān)鍵字?namespace,后跟命名空間的名稱,如下所示:
- 為了調(diào)用帶有命名空間的函數(shù)或變量,需要在前面加上命名空間的名稱,如下所示:
例子
#include <iostream> using namespace std;// 第一個命名空間 namespace first_space{void func(){cout << "Inside first_space" << endl;} } // 第二個命名空間 namespace second_space{void func(){cout << "Inside second_space" << endl;} } int main () {// 調(diào)用第一個命名空間中的函數(shù)first_space::func();// 調(diào)用第二個命名空間中的函數(shù)second_space::func(); return 0; }using指令
- 可以使用?using namespace?指令,這樣在使用命名空間時就可以不用在前面加上命名空間的名稱。這個指令會告訴編譯器,后續(xù)的代碼將使用指定的命名空間中的名稱。
?
#include <iostream> using namespace std;// 第一個命名空間 namespace first_space{void func(){cout << "Inside first_space" << endl;} } // 第二個命名空間 namespace second_space{void func(){cout << "Inside second_space" << endl;} } using namespace first_space; int main () {// 調(diào)用第一個命名空間中的函數(shù)func();return 0; }- using 指令也可以用來指定命名空間中的特定項目。例如,如果您只打算使用 std 命名空間中的 cout 部分,您可以使用如下的語句:
- using?指令引入的名稱遵循正常的范圍規(guī)則。名稱從使用?using?指令開始是可見的,直到該范圍結(jié)束。此時,在范圍以外定義的同名實體是隱藏的。
不連續(xù)的命名空間
- 命名空間可以定義在幾個不同的部分中,因此命名空間是由幾個單獨定義的部分組成的。一個命名空間的各個組成部分可以分散在多個文件中。
- 所以,如果命名空間中的某個組成部分需要請求定義在另一個文件中的名稱,則仍然需要聲明該名稱。下面的命名空間定義可以是定義一個新的命名空間,也可以是為已有的命名空間增加新的元素:
嵌套的命名空間
- 命名空間可以嵌套,您可以在一個命名空間中定義另一個命名空間,如下所示:
-
可以通過使用 :: 運算符來訪問嵌套的命名空間中的成員:
- 在上面的語句中,如果使用的是 namespace_name1,那么在該范圍內(nèi) namespace_name2 中的元素也是可用的,如下所示:
C++?模板
- 模板是泛型編程的基礎(chǔ),泛型編程即以一種獨立于任何特定類型的方式編寫代碼。
- 模板是創(chuàng)建泛型類或函數(shù)的藍(lán)圖或公式。庫容器,比如迭代器和算法,都是泛型編程的例子,它們都使用了模板的概念。每個容器都有一個單一的定義,比如?向量,可以定義許多不同類型的向量,比如?vector <int>?或?vector <string>。
函數(shù)模板
template <typename type> ret-type func-name(parameter list) {// 函數(shù)的主體 }-
在這里,type 是函數(shù)所使用的數(shù)據(jù)類型的占位符名稱。這個名稱可以在函數(shù)定義中使用。下面是函數(shù)模板的實例,返回兩個數(shù)中的最大值:
類模板
- 正如定義函數(shù)模板一樣,也可以定義類模板。泛型類聲明的一般形式如下所示:
- 在這里,type?是占位符類型名稱,可以在類被實例化的時候進(jìn)行指定。可以使用一個逗號分隔的列表來定義多個泛型數(shù)據(jù)類型。下面的實例定義了類 Stack<>,并實現(xiàn)了泛型方法來對元素進(jìn)行入棧出棧操作:
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的C++学习 高级编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言 共用体/联合体 union
- 下一篇: 深入理解Solidity 二