我們今天帶來了C++的基礎(chǔ)知識大匯總,當然這是精簡版的,但是篇幅也不少,應(yīng)該說該有的也都有了,建議大家收藏慢慢學習,同時希望對大家的C++學習有所幫助。
一 、從”hello world“ 入門C++!
C++總覽簡介
C++ 是一種靜態(tài)類型的、編譯式的、通用的、大小寫敏感的、不規(guī)則的編程語言,支持過程化編程、面向?qū)ο缶幊毯头盒途幊獭?div style="height:15px;">
C++ 被認為是一種中級語言,它綜合了高級語言和低級語言的特點。
C++ 是由 Bjarne Stroustrup 于 1979 年在新澤西州美利山貝爾實驗室開始設(shè)計開發(fā)的。C++ 進一步擴充和完善了 C 語言,最初命名為帶類的C,后來在 1983 年更名為 C++。
C++ 是 C 的一個超集,事實上,任何合法的 C 程序都是合法的 C++ 程序。
注意:使用靜態(tài)類型的編程語言是在編譯時執(zhí)行類型檢查,而不是在運行時執(zhí)行類型檢查。
面向?qū)ο蟪绦蛟O(shè)計
c++最大的亮點就是面向?qū)ο蟪绦蛟O(shè)計理念的運用。包括面向?qū)ο箝_發(fā)的四大特性:
封裝
抽象
繼承
多態(tài)
C++的組成部分
標準的 C++ 由三個重要部分組成:
核心語言,提供了所有構(gòu)件塊,包括變量、數(shù)據(jù)類型和常量,等等。
C++ 標準庫,提供了大量的函數(shù),用于操作文件、字符串等。
標準模板庫(STL),提供了大量的方法,用于操作數(shù)據(jù)結(jié)構(gòu)等。
C++的標準
對一門編程語言來說,遵循統(tǒng)一的標準是必需的。下面的表格列出了c++標準的發(fā)展歷史。
發(fā)布時間文檔通稱備注
2015ISO/IEC TS 19570:2015-用于并行計算的擴展
2015ISO/IEC TS 18822:2015-文件系統(tǒng)
2014ISO/IEC 14882:2014C++14第四個C++標準
2011ISO/IEC TR 24733:2011-十進制浮點數(shù)擴展
2011ISO/IEC 14882:2011C++11第三個C++標準
2010ISO/IEC TR 29124:2010-數(shù)學函數(shù)擴展
2007ISO/IEC TR 19768:2007C++TR1C++技術(shù)報告:庫擴展
2006ISO/IEC TR 18015:2006-C++性能技術(shù)報告
2003ISO/IEC 14882:2003C++03第二個C++標準
C++ 程序結(jié)構(gòu)
讓我們看一段簡單的代碼,可以輸出單詞 Hello World。
#include <iostream>using namespace std;// main() 是程序開始執(zhí)行的地方int main(){cout << 'Hello World'; // 輸出 Hello Worldreturn 0;}
讓我們一起來研究一下上面的代碼結(jié)構(gòu):
C++ 語言定義了一些頭文件,這些頭文件包含了程序中必需的或有用的信息。上面這段程序中,包含了頭文件 <iostream>。
下一行 using namespace std; 告訴編譯器使用 std 命名空間。命名空間是 C++ 中一個相對新的概念。
下一行 // main() 是程序開始執(zhí)行的地方 是一個單行注釋。單行注釋以 // 開頭,在行末結(jié)束。
下一行 int main() 是主函數(shù),程序從這里開始執(zhí)行。
下一行 cout << 'Hello World'; 會在屏幕上顯示消息 'Hello World'。
下一行 return 0; 終止 main( )函數(shù),并向調(diào)用進程返回值 0。
C++ 中的分號 & 語句塊
在 C++ 中,分號是語句結(jié)束符。也就是說,每個語句必須以分號結(jié)束。它表明一個邏輯實體的結(jié)束。
例如:下面就一共有兩條語句。
x = y;y = x+1;
語句塊是一組使用大括號括起來的按邏輯連接的語句。例如:
{cout << 'Hello World'; // 輸出 Hello Worldreturn 0;}
C++ 關(guān)鍵字
下表列出了 C++ 中的保留字。這些保留字不能作為常量名、變量名或其他標識符名稱。
asmelsenewthis
autoenumoperatorthrow
boolexplicitprivatetrue
breakexportprotectedtry
caseexternpublictypedef
catchfalseregistertypeid
charfloatreinterpret_casttypename
classforreturnunion
constfriendshortunsigned
const_castgotosignedusing
continueifsizeofvirtual
defaultinlinestaticvoid
deleteintstatic_castvolatile
dolongstructwchar_t
doublemutableswitchwhile
dynamic_castnamespacetemplate
C++ 標識符
C++ 標識符是用來標識變量、函數(shù)、類、模塊,或任何其他用戶自定義項目的名稱。一個標識符以字母 A-Z 或 a-z 或下劃線 _ 開始,后跟零個或多個字母、下劃線和數(shù)字(0-9),不允許改變標識符組成規(guī)則。
下面列出幾個有效的標識符:
test_temp二 、 c++基本數(shù)據(jù)類型及流控制語句詳解
C++ 數(shù)組
C++ 支持數(shù)組數(shù)據(jù)結(jié)構(gòu),它可以存儲一個固定大小的相同類型元素的順序集合。數(shù)組是用來存儲一系列數(shù)據(jù),但它往往被認為是一系列相同類型的變量。
數(shù)組的聲明并不是聲明一個個單獨的變量,比如 number0、number1、...、number99,而是聲明一個數(shù)組變量,比如 numbers,然后使用 numbers[0]、numbers[1]、...、numbers[99] 來代表一個個單獨的變量。數(shù)組中的特定元素可以通過索引訪問。
所有的數(shù)組都是由連續(xù)的內(nèi)存位置組成。最低的地址對應(yīng)第一個元素,最高的地址對應(yīng)最后一個元素。
聲明數(shù)組
在 C++ 中要聲明一個數(shù)組,需要指定元素的類型和元素的數(shù)量,如下所示:
type arrayName [ arraySize ];
這叫做一維數(shù)組。arraySize 必須是一個大于零的整數(shù)常量,type 可以是任意有效的 C++ 數(shù)據(jù)類型。例如,要聲明一個類型為 double 的包含 10 個元素的數(shù)組 balance,聲明語句如下:
double balance[10];
現(xiàn)在 balance 是一個可用的數(shù)組,可以容納 10 個類型為 double 的數(shù)字。
初始化數(shù)組
在 C++ 中,可以逐個初始化數(shù)組,也可以使用一個初始化語句,如下所示:
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
大括號 { } 之間的值的數(shù)目不能大于我們在數(shù)組聲明時在方括號 [ ] 中指定的元素數(shù)目。
如果省略掉了數(shù)組的大小,數(shù)組的大小則為初始化時元素的個數(shù)。因此,如果:
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
將創(chuàng)建一個數(shù)組,它與前一個實例中所創(chuàng)建的數(shù)組是完全相同的。
下面是一個為數(shù)組中某個元素賦值的實例:
balance[4] = 50.0;
上述的語句把數(shù)組中第五個元素的值賦為 50.0。所有的數(shù)組都是以 0 作為它們第一個元素的索引,也被稱為基索引,數(shù)組的最后一個索引是數(shù)組的總大小減去 1。以下是上面所討論的數(shù)組的的圖形表示:
訪問數(shù)組元素
數(shù)組元素可以通過數(shù)組名稱加索引進行訪問。元素的索引是放在方括號內(nèi),跟在數(shù)組名稱的后邊。例如:
double salary = balance[9];
上面的語句將把數(shù)組中第 10 個元素的值賦給 salary 變量。下面的實例使用了上述的三個概念,即,聲明數(shù)組、數(shù)組賦值、訪問數(shù)組:
#include <iostream>using namespace std; #include <iomanip>using std::setw; int main (){ int n[ 10 ]; // n 是一個包含 10 個整數(shù)的數(shù)組 // 初始化數(shù)組元素 for ( int i = 0; i < 10; i++ ) { n[ i ] = i + 100; // 設(shè)置元素 i 為 i + 100 } cout << 'Element' << setw( 13 ) << 'Value' << endl; // 輸出數(shù)組中每個元素的值 for ( int j = 0; j < 10; j++ ) { cout << setw( 7 )<< j << setw( 13 ) << n[ j ] << endl; } return 0;}
C++ 字符串
C++ 提供了以下兩種類型的字符串表示形式:
C 風格字符串
C++ 引入的 string 類類型
C 風格字符串
C 風格的字符串起源于 C 語言,并在 C++ 中繼續(xù)得到支持。字符串實際上是使用 null 字符 '\0' 終止的一維字符數(shù)組。因此,一個以 null 結(jié)尾的字符串,包含了組成字符串的字符。
下面的聲明和初始化創(chuàng)建了一個 'Hello' 字符串。由于在數(shù)組的末尾存儲了空字符,所以字符數(shù)組的大小比單詞 'Hello' 的字符數(shù)多一個。
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
依據(jù)數(shù)組初始化規(guī)則,可以把上面的語句寫成以下語句:
char greeting[] = 'Hello';
以下是 C/C++ 中定義的字符串的內(nèi)存表示:
其實,不需要把 null 字符放在字符串常量的末尾。C++ 編譯器會在初始化數(shù)組時,自動把 '\0' 放在字符串的末尾。讓我們嘗試輸出上面的字符串:
實例
#include <iostream> using namespace std; int main (){ char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; cout << 'Greeting message: '; cout << greeting << endl; return 0; }
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Greeting message: Hello
C++ 中有大量的函數(shù)用來操作以 null 結(jié)尾的字符串:supports a wide range of functions that manipulate null-terminated strings:
序號函數(shù) & 目的
1
strcpy(s1, s2); 復(fù)制字符串 s2 到字符串 s1。
2
strcat(s1, s2); 連接字符串 s2 到字符串 s1 的末尾。
3
strlen(s1); 返回字符串 s1 的長度。
4
strcmp(s1, s2); 如果 s1 和 s2 是相同的,則返回 0;如果 s1<s2 則返回值小于 0;如果 s1>s2 則返回值大于 0。
5
strchr(s1, ch); 返回一個指針,指向字符串 s1 中字符 ch 的第一次出現(xiàn)的位置。
6
strstr(s1, s2); 返回一個指針,指向字符串 s1 中字符串 s2 的第一次出現(xiàn)的位置。
下面的實例使用了上述的一些函數(shù):
實例
#include <iostream>#include <cstring> using namespace std; int main (){ char str1[11] = 'Hello'; char str2[11] = 'World'; char str3[11]; int len ; // 復(fù)制 str1 到 str3 strcpy( str3, str1); cout << 'strcpy( str3, str1) : ' << str3 << endl; // 連接 str1 和 str2 strcat( str1, str2); cout << 'strcat( str1, str2): ' << str1 << endl; // 連接后,str1 的總長度 len = strlen(str1); cout << 'strlen(str1) : ' << len << endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
strcpy( str3, str1) : Hellostrcat( str1, str2): HelloWorldstrlen(str1) : 10C++ 中的 String 類
C++ 中的 string 類
C++ 標準庫提供了 string 類類型,支持上述所有的操作,另外還增加了其他更多的功能。現(xiàn)在讓我們先來看看下面這個實例:
實例
#include <iostream>#include <string> using namespace std; int main (){ string str1 = 'Hello'; string str2 = 'World'; string str3; int len ; // 復(fù)制 str1 到 str3 str3 = str1; cout << 'str3 : ' << str3 << endl; // 連接 str1 和 str2 str3 = str1 + str2; cout << 'str1 + str2 : ' << str3 << endl; // 連接后,str3 的總長度 len = str3.size(); cout << 'str3.size() : ' << len << endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
str3 : Hellostr1 + str2 : HelloWorldstr3.size() : 10
C++ 循環(huán)
有的時候,可能需要多次執(zhí)行同一塊代碼。一般情況下,語句是順序執(zhí)行的:函數(shù)中的第一個語句先執(zhí)行,接著是第二個語句,依此類推。
編程語言提供了允許更為復(fù)雜的執(zhí)行路徑的多種控制結(jié)構(gòu)。
循環(huán)語句允許我們多次執(zhí)行一個語句或語句組,下面是大多數(shù)編程語言中循環(huán)語句的一般形式:
循環(huán)類型
C++ 編程語言提供了以下幾種循環(huán)類型。
循環(huán)類型描述
while 循環(huán)
當給定條件為真時,重復(fù)語句或語句組。它會在執(zhí)行循環(huán)主體之前測試條件。
for 循環(huán)
多次執(zhí)行一個語句序列,簡化管理循環(huán)變量的代碼。
do...while 循環(huán)
除了它是在循環(huán)主體結(jié)尾測試條件外,其他與 while 語句類似。
嵌套循環(huán)
可以在 while、for 或 do..while 循環(huán)內(nèi)使用一個或多個循環(huán)。
循環(huán)控制語句
循環(huán)控制語句更改執(zhí)行的正常序列。當執(zhí)行離開一個范圍時,所有在該范圍中創(chuàng)建的自動對象都會被銷毀。
C++ 提供了下列的控制語句。
控制語句描述
break 語句
終止 loop 或 switch 語句,程序流將繼續(xù)執(zhí)行緊接著 loop 或 switch 的下一條語句。
continue 語句
引起循環(huán)跳過主體的剩余部分,立即重新開始測試條件。
goto 語句
將控制轉(zhuǎn)移到被標記的語句。但是不建議在程序中使用 goto 語句。
無限循環(huán)
如果條件永遠不為假,則循環(huán)將變成無限循環(huán)。for 循環(huán)在傳統(tǒng)意義上可用于實現(xiàn)無限循環(huán)。由于構(gòu)成循環(huán)的三個表達式中任何一個都不是必需的,可以將某些條件表達式留空來構(gòu)成一個無限循環(huán)。
實例
#include <iostream>using namespace std; int main (){ for( ; ; ) { printf('This loop will run forever.\n'); } return 0;}
當條件表達式不存在時,它被假設(shè)為真。也可以設(shè)置一個初始值和增量表達式,但是一般情況下,C++ 程序員偏向于使用 for(;;) 結(jié)構(gòu)來表示一個無限循環(huán)。
注意:可以按 Ctrl + C 鍵終止一個無限循環(huán)。
C++ 判斷
判斷結(jié)構(gòu)要求程序員指定一個或多個要評估或測試的條件,以及條件為真時要執(zhí)行的語句(必需的)和條件為假時要執(zhí)行的語句(可選的)。
下面是大多數(shù)編程語言中典型的判斷結(jié)構(gòu)的一般形式:
判斷語句
C++ 編程語言提供了以下類型的判斷語句。
語句描述
if 語句
一個 if 語句 由一個布爾表達式后跟一個或多個語句組成。
if...else 語句
一個 if 語句 后可跟一個可選的 else 語句,else 語句在布爾表達式為假時執(zhí)行。
嵌套 if 語句
可以在一個 if 或 else if 語句內(nèi)使用另一個 if 或 else if 語句。
switch 語句
一個 switch 語句允許測試一個變量等于多個值時的情況。
嵌套 switch 語句
可以在一個 switch 語句內(nèi)使用另一個 switch 語句。
三 、 c++進階 基本輸入輸出、指針、引用
C++ 基本的輸入輸出
今天我們來熟悉一下C++ 編程中最基本和最常見的 I/O 操作。
C++ 的 I/O 發(fā)生在流中,流是字節(jié)序列。如果字節(jié)流是從設(shè)備(如鍵盤、磁盤驅(qū)動器、網(wǎng)絡(luò)連接等)流向內(nèi)存,這叫做輸入操作。如果字節(jié)流是從內(nèi)存流向設(shè)備(如顯示屏、打印機、磁盤驅(qū)動器、網(wǎng)絡(luò)連接等),這叫做輸出操作。
I/O 庫頭文件
下列的頭文件在 C++ 編程中很重要。
頭文件
函數(shù)和描述
<iostream>
該文件定義了 cin、cout、cerr 和 clog 對象,分別對應(yīng)于標準輸入流、標準輸出流、非緩沖標準錯誤流和緩沖標準錯誤流。
<iomanip>
該文件通過所謂的參數(shù)化的流操縱器(比如 setw 和 setprecision),來聲明對執(zhí)行標準化 I/O 有用的服務(wù)。
<fstream>
該文件為用戶控制的文件處理聲明服務(wù)。我們將在文件和流的相關(guān)章節(jié)討論它的細節(jié)。
標準輸出流(cout)
預(yù)定義的對象 cout 是 iostream 類的一個實例。cout 對象'連接'到標準輸出設(shè)備,通常是顯示屏。cout 是與流插入運算符 << 結(jié)合使用的,如下所示:
實例
#include <iostream> using namespace std; int main( ){ char str[] = 'Hello C++'; cout << 'Value of str is : ' << str << endl;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Value of str is : Hello C++
C++ 編譯器根據(jù)要輸出變量的數(shù)據(jù)類型,選擇合適的流插入運算符來顯示值。<< 運算符被重載來輸出內(nèi)置類型(整型、浮點型、double 型、字符串和指針)的數(shù)據(jù)項。
流插入運算符 << 在一個語句中可以多次使用,如上面實例中所示,endl 用于在行末添加一個換行符。
標準輸入流(cin)
預(yù)定義的對象 cin 是 iostream 類的一個實例。cin 對象附屬到標準輸入設(shè)備,通常是鍵盤。cin 是與流提取運算符 >> 結(jié)合使用的,如下所示:
實例
#include <iostream> using namespace std; int main( ){ char name[50]; cout << '請輸入您的名稱:'; cin >> name; cout << '您的名稱是:' << name << endl; }
當上面的代碼被編譯和執(zhí)行時,它會提示用戶輸入名稱。當用戶輸入一個值,并按回車鍵,就會看到下列結(jié)果:
請輸入您的名稱:cplusplus您的名稱是:cplusplus
C++ 編譯器根據(jù)要輸入值的數(shù)據(jù)類型,選擇合適的流提取運算符來提取值,并把它存儲在給定的變量中。
流提取運算符 >> 在一個語句中可以多次使用,如果要求輸入多個數(shù)據(jù),可以使用如下語句:
cin >> name >> age;
這相當于下面兩個語句:
cin >> name;cin >> age;
標準錯誤流(cerr)
預(yù)定義的對象 cerr 是 iostream 類的一個實例。cerr 對象附屬到標準錯誤設(shè)備,通常也是顯示屏,但是 cerr 對象是非緩沖的,且每個流插入到 cerr 都會立即輸出。
cerr 也是與流插入運算符 << 結(jié)合使用的,如下所示:
實例
#include <iostream> using namespace std; int main( ){ char str[] = 'Unable to read....'; cerr << 'Error message : ' << str << endl;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Error message : Unable to read....
標準日志流(clog)
預(yù)定義的對象 clog 是 iostream 類的一個實例。clog 對象附屬到標準錯誤設(shè)備,通常也是顯示屏,但是 clog 對象是緩沖的。這意味著每個流插入到 clog 都會先存儲在緩沖在,直到緩沖填滿或者緩沖區(qū)刷新時才會輸出。
clog 也是與流插入運算符 << 結(jié)合使用的,如下所示:
實例
#include <iostream> using namespace std; int main( ){ char str[] = 'Unable to read....'; clog << 'Error message : ' << str << endl;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Error message : Unable to read....
通過這些小實例,我們無法區(qū)分 cout、cerr 和 clog 的差異,但在編寫和執(zhí)行大型程序時,它們之間的差異就變得非常明顯。所以良好的編程實踐告訴我們,使用 cerr 流來顯示錯誤消息,而其他的日志消息則使用 clog 流來輸出。
C++ 指針
學習 C++ 的指針既簡單又有趣。通過指針,可以簡化一些 C++ 編程任務(wù)的執(zhí)行,還有一些任務(wù),如動態(tài)內(nèi)存分配,沒有指針是無法執(zhí)行的。所以,想要成為一名優(yōu)秀的 C++ 程序員,學習指針是很有必要的。
每一個變量都有一個內(nèi)存位置,每一個內(nèi)存位置都定義了可使用連字號(&)運算符訪問的地址,它表示了在內(nèi)存中的一個地址。請看下面的實例,它將輸出定義的變量地址:
實例
#include <iostream> using namespace std; int main (){ int var1; char var2[10]; cout << 'var1 變量的地址:'; cout << &var1 << endl; cout << 'var2 變量的地址:'; cout << &var2 << endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
var1 變量的地址:0xbfebd5c0var2 變量的地址:0xbfebd5b6
通過上面的實例,我們了解了什么是內(nèi)存地址以及如何訪問它。接下來讓我們看看什么是指針。
什么是指針?
指針是一個變量,其值為另一個變量的地址,即,內(nèi)存位置的直接地址。就像其他變量或常量一樣,必須在使用指針存儲其他變量地址之前,對其進行聲明。指針變量聲明的一般形式為:
type *var-name;
在這里,type 是指針的基類型,它必須是一個有效的 C++ 數(shù)據(jù)類型,var-name 是指針變量的名稱。用來聲明指針的星號 * 與乘法中使用的星號是相同的。但是,在這個語句中,星號是用來指定一個變量是指針。以下是有效的指針聲明:
int *ip; /* 一個整型的指針 */double *dp; /* 一個 double 型的指針 */float *fp; /* 一個浮點型的指針 */char *ch; /* 一個字符型的指針 */
所有指針的值的實際數(shù)據(jù)類型,不管是整型、浮點型、字符型,還是其他的數(shù)據(jù)類型,都是一樣的,都是一個代表內(nèi)存地址的長的十六進制數(shù)。不同數(shù)據(jù)類型的指針之間唯一的不同是,指針所指向的變量或常量的數(shù)據(jù)類型不同。
C++ 中使用指針
使用指針時會頻繁進行以下幾個操作:定義一個指針變量、把變量地址賦值給指針、訪問指針變量中可用地址的值。這些是通過使用一元運算符 *來返回位于操作數(shù)所指定地址的變量的值。下面的實例涉及到了這些操作:
實例
#include <iostream> using namespace std; int main (){ int var = 20; // 實際變量的聲明 int *ip; // 指針變量的聲明 ip = &var; // 在指針變量中存儲 var 的地址 cout << 'Value of var variable: '; cout << var << endl; // 輸出在指針變量中存儲的地址 cout << 'Address stored in ip variable: '; cout << ip << endl; // 訪問指針中地址的值 cout << 'Value of *ip variable: '; cout << *ip << endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Value of var variable: 20Address stored in ip variable: 0xbfc601acValue of *ip variable: 20
C++ 引用
引用變量是一個別名,也就是說,它是某個已存在變量的另一個名字。一旦把引用初始化為某個變量,就可以使用該引用名稱或變量名稱來指向變量。
C++ 引用 vs 指針
引用很容易與指針混淆,它們之間有三個主要的不同:
不存在空引用。引用必須連接到一塊合法的內(nèi)存。
一旦引用被初始化為一個對象,就不能被指向到另一個對象。指針可以在任何時候指向到另一個對象。
引用必須在創(chuàng)建時被初始化。指針可以在任何時間被初始化。
C++ 中創(chuàng)建引用
試想變量名稱是變量附屬在內(nèi)存位置中的標簽,可以把引用當成是變量附屬在內(nèi)存位置中的第二個標簽。因此,可以通過原始變量名稱或引用來訪問變量的內(nèi)容。例如:
int i = 17;
我們可以為 i 聲明引用變量,如下所示:
int& r = i;double& s = d;
在這些聲明中,& 讀作引用。因此,第一個聲明可以讀作 'r 是一個初始化為 i 的整型引用',第二個聲明可以讀作 's 是一個初始化為 d 的 double 型引用'。下面的實例使用了 int 和 double 引用:
實例
#include <iostream> using namespace std; int main (){ // 聲明簡單的變量 int i; double d; // 聲明引用變量 int& r = i; double& s = d; i = 5; cout << 'Value of i : ' << i << endl; cout << 'Value of i reference : ' << r << endl; d = 11.7; cout << 'Value of d : ' << d << endl; cout << 'Value of d reference : ' << s << endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Value of i : 5Value of i reference : 5Value of d : 11.7Value of d reference : 11.7
引用通常用于函數(shù)參數(shù)列表和函數(shù)返回值。下面列出了 C++ 程序員必須清楚的兩個與 C++ 引用相關(guān)的重要概念:
概念描述
把引用作為參數(shù)
C++ 支持把引用作為參數(shù)傳給函數(shù),這比傳一般的參數(shù)更安全。
把引用作為返回值
可以從 C++ 函數(shù)中返回引用,就像返回其他數(shù)據(jù)類型一樣。
四、 C++ 類與對象初探:繼承和重載
C++ 類 & 對象
C++ 在 C 語言的基礎(chǔ)上增加了面向?qū)ο缶幊蹋珻++ 支持面向?qū)ο蟪绦蛟O(shè)計。類是 C++ 的核心特性,通常被稱為用戶定義的類型。
類用于指定對象的形式,它包含了數(shù)據(jù)表示法和用于處理數(shù)據(jù)的方法。類中的數(shù)據(jù)和方法稱為類的成員。函數(shù)在一個類中被稱為類的成員。
C++ 類定義
定義一個類,本質(zhì)上是定義一個數(shù)據(jù)類型的藍圖。這實際上并沒有定義任何數(shù)據(jù),但它定義了類的名稱意味著什么,也就是說,它定義了類的對象包括了什么,以及可以在這個對象上執(zhí)行哪些操作。
類定義是以關(guān)鍵字 class 開頭,后跟類的名稱。類的主體是包含在一對花括號中。類定義后必須跟著一個分號或一個聲明列表。例如,我們使用關(guān)鍵字 class 定義 Box 數(shù)據(jù)類型,如下所示:
class Box{ public: double length; // 盒子的長度 double breadth; // 盒子的寬度 double height; // 盒子的高度};
關(guān)鍵字 public 確定了類成員的訪問屬性。在類對象作用域內(nèi),公共成員在類的外部是可訪問的。也可以指定類的成員為 private 或 protected。
定義 C++ 對象
類提供了對象的藍圖,所以基本上,對象是根據(jù)類來創(chuàng)建的。聲明類的對象,就像聲明基本類型的變量一樣。下面的語句聲明了類 Box 的兩個對象:
Box Box1; // 聲明 Box1,類型為 Box Box Box2; // 聲明 Box2,類型為 Box對象 Box1 和 Box2 都有它們各自的數(shù)據(jù)成員。
訪問數(shù)據(jù)成員
類的對象的公共數(shù)據(jù)成員可以使用直接成員訪問運算符 (.) 來訪問。為了更好地理解這些概念,讓我們嘗試一下下面的實例:
實例
#include <iostream> using namespace std; class Box{ public: double length; // 長度 double breadth; // 寬度 double height; // 高度}; int main( ){ Box Box1; // 聲明 Box1,類型為 Box Box Box2; // 聲明 Box2,類型為 Box double volume = 0.0; // 用于存儲體積 // box 1 詳述 Box1.height = 5.0; Box1.length = 6.0; Box1.breadth = 7.0; // box 2 詳述 Box2.height = 10.0; Box2.length = 12.0; Box2.breadth = 13.0; // box 1 的體積 volume = Box1.height * Box1.length * Box1.breadth; cout << 'Box1 的體積:' << volume <<endl; // box 2 的體積 volume = Box2.height * Box2.length * Box2.breadth; cout << 'Box2 的體積:' << volume <<endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Box1 的體積:210Box2 的體積:1560
需要注意的是,私有的成員和受保護的成員不能使用直接成員訪問運算符 (.) 來直接訪問。
C++ 繼承
面向?qū)ο蟪绦蛟O(shè)計中最重要的一個概念是繼承。繼承允許我們依據(jù)另一個類來定義一個類,這使得創(chuàng)建和維護一個應(yīng)用程序變得更容易。這樣做,也達到了重用代碼功能和提高執(zhí)行時間的效果。
當創(chuàng)建一個類時,您不需要重新編寫新的數(shù)據(jù)成員和成員函數(shù),只需指定新建的類繼承了一個已有的類的成員即可。這個已有的類稱為基類,新建的類稱為派生類。
繼承代表了 is a 關(guān)系。例如,哺乳動物是動物,狗是哺乳動物,因此,狗是動物,等等。
基類 & 派生類
一個類可以派生自多個類,這意味著,它可以從多個基類繼承數(shù)據(jù)和函數(shù)。定義一個派生類,我們使用一個類派生列表來指定基類。類派生列表以一個或多個基類命名,形式如下:
class derived-class: access-specifier base-class
其中,訪問修飾符 access-specifier 是 public、protected 或 private 其中的一個,base-class 是之前定義過的某個類的名稱。如果未使用訪問修飾符 access-specifier,則默認為 private。
假設(shè)有一個基類 Shape,Rectangle 是它的派生類,如下所示:
實例
#include <iostream> using namespace std; // 基類class Shape { public: void setWidth(int w){ width = w; } void setHeight(int h){ height = h; } protected: int width; int height;}; // 派生類class Rectangle: public Shape{ public: int getArea(){ return (width * height); }}; int main(void){ Rectangle Rect; Rect.setWidth(5); Rect.setHeight(7); // 輸出對象的面積 cout << 'Total area: ' << Rect.getArea() << endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Total area: 35
訪問控制和繼承
派生類可以訪問基類中所有的非私有成員。因此基類成員如果不想被派生類的成員函數(shù)訪問,則應(yīng)在基類中聲明為 private。
我們可以根據(jù)訪問權(quán)限總結(jié)出不同的訪問類型,如下所示:
訪問publicprotectedprivate
同一個類
yes
yes
yes
派生類
yes
yes
no
外部的類
yes
no
no
一個派生類繼承了所有的基類方法,但下列情況除外:
基類的構(gòu)造函數(shù)、析構(gòu)函數(shù)和拷貝構(gòu)造函數(shù)。
基類的重載運算符。
基類的友元函數(shù)。
繼承類型
當一個類派生自基類,該基類可以被繼承為 public、protected 或 private 幾種類型。繼承類型是通過上面講解的訪問修飾符 access-specifier 來指定的。
我們幾乎不使用 protected 或 private 繼承,通常使用 public 繼承。當使用不同類型的繼承時,遵循以下幾個規(guī)則:
公有繼承(public):當一個類派生自公有基類時,基類的公有成員也是派生類的公有成員,基類的保護成員也是派生類的保護成員,基類的私有成員不能直接被派生類訪問,但是可以通過調(diào)用基類的公有和保護成員來訪問。
保護繼承(protected):當一個類派生自保護基類時,基類的公有和保護成員將成為派生類的保護成員。
私有繼承(private):當一個類派生自私有基類時,基類的公有和保護成員將成為派生類的私有成員。
多繼承
多繼承即一個子類可以有多個父類,它繼承了多個父類的特性。
C++ 類可以從多個類繼承成員,語法如下:
class <派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>,…{<派生類類體>};
其中,訪問修飾符繼承方式是 public、protected 或 private 其中的一個,用來修飾每個基類,各個基類之間用逗號分隔,如上所示。現(xiàn)在讓我們一起看看下面的實例:
實例
#include <iostream> using namespace std; // 基類 Shapeclass Shape { public: void setWidth(int w){ width = w; } void setHeight(int h){ height = h; } protected: int width; int height;}; // 基類 PaintCostclass PaintCost { public: int getCost(int area){ return area * 70; }}; // 派生類class Rectangle: public Shape, public PaintCost{ public: int getArea(){ return (width * height); }}; int main(void){ Rectangle Rect; int area; Rect.setWidth(5); Rect.setHeight(7); area = Rect.getArea(); // 輸出對象的面積 cout << 'Total area: ' << Rect.getArea() << endl; // 輸出總花費 cout << 'Total paint cost: $' << Rect.getCost(area) << endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Total area: 35Total paint cost: $2450
C++ 重載運算符和重載函數(shù)
C++ 允許在同一作用域中的某個函數(shù)和運算符指定多個定義,分別稱為函數(shù)重載和運算符重載。
重載聲明是指一個與之前已經(jīng)在該作用域內(nèi)聲明過的函數(shù)或方法具有相同名稱的聲明,但是它們的參數(shù)列表和定義(實現(xiàn))不相同。
當您調(diào)用一個重載函數(shù)或重載運算符時,編譯器通過把您所使用的參數(shù)類型與定義中的參數(shù)類型進行比較,決定選用最合適的定義。選擇最合適的重載函數(shù)或重載運算符的過程,稱為重載決策。
C++ 中的函數(shù)重載
在同一個作用域內(nèi),可以聲明幾個功能類似的同名函數(shù),但是這些同名函數(shù)的形式參數(shù)(指參數(shù)的個數(shù)、類型或者順序)必須不同。您不能僅通過返回類型的不同來重載函數(shù)。
下面的實例中,同名函數(shù) print() 被用于輸出不同的數(shù)據(jù)類型:
實例
#include <iostream>using namespace std; class printData{ public: void print(int i) { cout << '整數(shù)為: ' << i << endl; } void print(double f) { cout << '浮點數(shù)為: ' << f << endl; } void print(char c[]) { cout << '字符串為: ' << c << endl; }}; int main(void){ printData pd; // 輸出整數(shù) pd.print(5); // 輸出浮點數(shù) pd.print(500.263); // 輸出字符串 char c[] = 'Hello C++'; pd.print(c); return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
整數(shù)為: 5浮點數(shù)為: 500.263字符串為: Hello C++
C++ 中的運算符重載
您可以重定義或重載大部分 C++ 內(nèi)置的運算符。這樣,您就能使用自定義類型的運算符。
重載的運算符是帶有特殊名稱的函數(shù),函數(shù)名是由關(guān)鍵字 operator 和其后要重載的運算符符號構(gòu)成的。與其他函數(shù)一樣,重載運算符有一個返回類型和一個參數(shù)列表。
Box operator+(const Box&);
聲明加法運算符用于把兩個 Box 對象相加,返回最終的 Box 對象。大多數(shù)的重載運算符可被定義為普通的非成員函數(shù)或者被定義為類成員函數(shù)。如果我們定義上面的函數(shù)為類的非成員函數(shù),那么我們需要為每次操作傳遞兩個參數(shù),如下所示:
Box operator+(const Box&, const Box&);
下面的實例使用成員函數(shù)演示了運算符重載的概念。在這里,對象作為參數(shù)進行傳遞,對象的屬性使用 this 運算符進行訪問,如下所示:
實例
#include <iostream>using namespace std; class Box{ public: double getVolume(void){ return length * breadth * height; } void setLength( double len ){ length = len; } void setBreadth( double bre ){ breadth = bre; } void setHeight( double hei ){ height = hei; } // 重載 + 運算符,用于把兩個 Box 對象相加 Box operator+(const Box& b) { Box box; box.length = this->length + b.length; box.breadth = this->breadth + b.breadth; box.height = this->height + b.height; return box; } private: double length; // 長度 double breadth; // 寬度 double height; // 高度};// 程序的主函數(shù)int main( ){ Box Box1; // 聲明 Box1,類型為 Box Box Box2; // 聲明 Box2,類型為 Box Box Box3; // 聲明 Box3,類型為 Box double volume = 0.0; // 把體積存儲在該變量中 // Box1 詳述 Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0); // Box2 詳述 Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0); // Box1 的體積 volume = Box1.getVolume(); cout << 'Volume of Box1 : ' << volume <<endl; // Box2 的體積 volume = Box2.getVolume(); cout << 'Volume of Box2 : ' << volume <<endl; // 把兩個對象相加,得到 Box3 Box3 = Box1 + Box2; // Box3 的體積 volume = Box3.getVolume(); cout << 'Volume of Box3 : ' << volume <<endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Volume of Box1 : 210Volume of Box2 : 1560Volume of Box3 : 5400五 、C++進階 多態(tài)和數(shù)據(jù)抽象
C++ 多態(tài)
多態(tài)按字面的意思就是多種形態(tài)。當類之間存在層次結(jié)構(gòu),并且類之間是通過繼承關(guān)聯(lián)時,就會用到多態(tài)。
C++ 多態(tài)意味著調(diào)用成員函數(shù)時,會根據(jù)調(diào)用函數(shù)的對象的類型來執(zhí)行不同的函數(shù)。
下面的實例中,基類 Shape 被派生為兩個類,如下所示:
實例
#include <iostream> using namespace std; class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } int area(){ cout << 'Parent class area :' <<endl; return 0; }};class Rectangle: public Shape{ public: Rectangle( int a=0, int b=0):Shape(a, b) { } int area (){ cout << 'Rectangle class area :' <<endl; return (width * height); }};class Triangle: public Shape{ public: Triangle( int a=0, int b=0):Shape(a, b) { } int area (){ cout << 'Triangle class area :' <<endl; return (width * height / 2); }};// 程序的主函數(shù)int main( ){ Shape *shape; Rectangle rec(10,7); Triangle tri(10,5); // 存儲矩形的地址 shape = &rec; // 調(diào)用矩形的求面積函數(shù) area shape->area(); // 存儲三角形的地址 shape = &tri; // 調(diào)用三角形的求面積函數(shù) area shape->area(); return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Parent class areaParent class area
導(dǎo)致錯誤輸出的原因是,調(diào)用函數(shù) area() 被編譯器設(shè)置為基類中的版本,這就是所謂的靜態(tài)多態(tài),或靜態(tài)鏈接 - 函數(shù)調(diào)用在程序執(zhí)行前就準備好了。有時候這也被稱為早綁定,因為 area() 函數(shù)在程序編譯期間就已經(jīng)設(shè)置好了。
但現(xiàn)在,讓我們對程序稍作修改,在 Shape 類中,area() 的聲明前放置關(guān)鍵字 virtual,如下所示:
class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } virtual int area(){ cout << 'Parent class area :' <<endl; return 0; }};
修改后,當編譯和執(zhí)行前面的實例代碼時,它會產(chǎn)生以下結(jié)果:
Rectangle class areaTriangle class area
此時,編譯器看的是指針的內(nèi)容,而不是它的類型。因此,由于 tri 和 rec 類的對象的地址存儲在 *shape 中,所以會調(diào)用各自的 area() 函數(shù)。
正如大家所看到的,每個子類都有一個函數(shù) area() 的獨立實現(xiàn)。這就是多態(tài)的一般使用方式。有了多態(tài),就可以有多個不同的類,都帶有同一個名稱但具有不同實現(xiàn)的函數(shù),函數(shù)的參數(shù)甚至可以是相同的。
虛函數(shù)
虛函數(shù) 是在基類中使用關(guān)鍵字 virtual 聲明的函數(shù)。在派生類中重新定義基類中定義的虛函數(shù)時,會告訴編譯器不要靜態(tài)鏈接到該函數(shù)。
我們想要的是在程序中任意點可以根據(jù)所調(diào)用的對象類型來選擇調(diào)用的函數(shù),這種操作被稱為動態(tài)鏈接,或后期綁定。
純虛函數(shù)
大家可能想要在基類中定義虛函數(shù),以便在派生類中重新定義該函數(shù)更好地適用于對象,但是大家在基類中又不能對虛函數(shù)給出有意義的實現(xiàn),這個時候就會用到純虛函數(shù)。
我們可以把基類中的虛函數(shù) area() 改寫如下:
class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } // pure virtual function virtual int area() = 0;};
= 0 告訴編譯器,函數(shù)沒有主體,上面的虛函數(shù)是純虛函數(shù)。
C++ 數(shù)據(jù)抽象
數(shù)據(jù)抽象是指,只向外界提供關(guān)鍵信息,并隱藏其后臺的實現(xiàn)細節(jié),即只表現(xiàn)必要的信息而不呈現(xiàn)細節(jié)。
數(shù)據(jù)抽象是一種依賴于接口和實現(xiàn)分離的編程(設(shè)計)技術(shù)。
讓我們舉一個現(xiàn)實生活中的真實例子,比如一臺電視機,可以打開和關(guān)閉、切換頻道、調(diào)整音量、添加外部組件(如喇叭、錄像機、DVD 播放器),但是我們不知道它的內(nèi)部實現(xiàn)細節(jié),也就是說,我們并不知道它是如何通過纜線接收信號,如何轉(zhuǎn)換信號,并最終顯示在屏幕上。
因此,我們可以說電視把它的內(nèi)部實現(xiàn)和外部接口分離開了,我們無需知道它的內(nèi)部實現(xiàn)原理,直接通過它的外部接口(比如電源按鈕、遙控器、聲量控制器)就可以操控電視。
現(xiàn)在,讓我們言歸正傳,就 C++ 編程而言,C++ 類為數(shù)據(jù)抽象提供了可能。它們向外界提供了大量用于操作對象數(shù)據(jù)的公共方法,也就是說,外界實際上并不清楚類的內(nèi)部實現(xiàn)。
例如,程序可以調(diào)用 sort() 函數(shù),而不需要知道函數(shù)中排序數(shù)據(jù)所用到的算法。實際上,函數(shù)排序的底層實現(xiàn)會因庫的版本不同而有所差異,只要接口不變,函數(shù)調(diào)用就可以照常工作。
在 C++ 中,我們使用類來定義我們自己的抽象數(shù)據(jù)類型(ADT)。可以使用類 iostream 的 cout 對象來輸出數(shù)據(jù)到標準輸出,如下所示:
實例
#include <iostream>using namespace std; int main( ){ cout << 'Hello C++' <<endl; return 0;}
在這里,暫時不需要理解 cout 是如何在用戶的屏幕上顯示文本。只需要知道公共接口即可,cout 的底層實現(xiàn)可以自由改變。
訪問標簽強制抽象
在 C++ 中,我們使用訪問標簽來定義類的抽象接口。一個類可以包含零個或多個訪問標簽:
使用公共標簽定義的成員都可以訪問該程序的所有部分。一個類型的數(shù)據(jù)抽象視圖是由它的公共成員來定義的。
使用私有標簽定義的成員無法訪問到使用類的代碼。私有部分對使用類型的代碼隱藏了實現(xiàn)細節(jié)。
訪問標簽出現(xiàn)的頻率沒有限制。每個訪問標簽指定了緊隨其后的成員定義的訪問級別。指定的訪問級別會一直有效,直到遇到下一個訪問標簽或者遇到類主體的關(guān)閉右括號為止。
數(shù)據(jù)抽象的好處
數(shù)據(jù)抽象有兩個重要的優(yōu)勢:
類的內(nèi)部受到保護,不會因無意的用戶級錯誤導(dǎo)致對象狀態(tài)受損。
類實現(xiàn)可能隨著時間的推移而發(fā)生變化,以便應(yīng)對不斷變化的需求,或者應(yīng)對那些要求不改變用戶級代碼的錯誤報告。
如果只在類的私有部分定義數(shù)據(jù)成員,編寫該類的作者就可以隨意更改數(shù)據(jù)。如果實現(xiàn)發(fā)生改變,則只需要檢查類的代碼,看看這個改變會導(dǎo)致哪些影響。
如果數(shù)據(jù)是公有的,則任何直接訪問舊表示形式的數(shù)據(jù)成員的函數(shù)都可能受到影響。
數(shù)據(jù)抽象的實例
C++ 程序中,任何帶有公有和私有成員的類都可以作為數(shù)據(jù)抽象的實例。請看下面的實例:
實例
#include <iostream>using namespace std; class Adder{ public: // 構(gòu)造函數(shù) Adder(int i = 0) { total = i; } // 對外的接口 void addNum(int number){ total += number; } // 對外的接口 int getTotal(){ return total; }; private: // 對外隱藏的數(shù)據(jù) int total;};int main( ){ Adder a; a.addNum(10); a.addNum(20); a.addNum(30); cout << 'Total ' << a.getTotal() <<endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Total 60
上面的類把數(shù)字相加,并返回總和。公有成員 addNum 和 getTotal 是對外的接口,用戶需要知道它們以便使用類。私有成員 total 是用戶不需要了解的,但又是類能正常工作所必需的。
C++ 數(shù)據(jù)封裝
所有的 C++ 程序都有以下兩個基本要素:
程序語句(代碼):這是程序中執(zhí)行動作的部分,它們被稱為函數(shù)。
程序數(shù)據(jù):數(shù)據(jù)是程序的信息,會受到程序函數(shù)的影響。
封裝是面向?qū)ο缶幊讨械陌褦?shù)據(jù)和操作數(shù)據(jù)的函數(shù)綁定在一起的一個概念,這樣能避免受到外界的干擾和誤用,從而確保了安全。數(shù)據(jù)封裝引申出了另一個重要的 OOP 概念,即數(shù)據(jù)隱藏。
數(shù)據(jù)封裝是一種把數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)捆綁在一起的機制,數(shù)據(jù)抽象是一種僅向用戶暴露接口而把具體的實現(xiàn)細節(jié)隱藏起來的機制。
C++ 通過創(chuàng)建類來支持封裝和數(shù)據(jù)隱藏(public、protected、private)。我們已經(jīng)知道,類包含私有成員(private)、保護成員(protected)和公有成員(public)成員。默認情況下,在類中定義的所有項目都是私有的。例如:
class Box{ public: double getVolume(void) { return length * breadth * height; } private: double length; // 長度 double breadth; // 寬度 double height; // 高度};
變量 length、breadth 和 height 都是私有的(private)。這意味著它們只能被 Box 類中的其他成員訪問,而不能被程序中其他部分訪問。這是實現(xiàn)封裝的一種方式。
為了使類中的成員變成公有的(即,程序中的其他部分也能訪問),必須在這些成員前使用 public 關(guān)鍵字進行聲明。所有定義在 public 標識符后邊的變量或函數(shù)可以被程序中所有其他的函數(shù)訪問。
把一個類定義為另一個類的友元類,會暴露實現(xiàn)細節(jié),從而降低了封裝性。理想的做法是盡可能地對外隱藏每個類的實現(xiàn)細節(jié)。
數(shù)據(jù)封裝的實例
C++ 程序中,任何帶有公有和私有成員的類都可以作為數(shù)據(jù)封裝和數(shù)據(jù)抽象的實例。請看下面的實例:
實例
#include <iostream>using namespace std; class Adder{ public: // 構(gòu)造函數(shù) Adder(int i = 0) { total = i; } // 對外的接口 void addNum(int number){ total += number; } // 對外的接口 int getTotal(){ return total; }; private: // 對外隱藏的數(shù)據(jù) int total;};int main( ){ Adder a; a.addNum(10); a.addNum(20); a.addNum(30); cout << 'Total ' << a.getTotal() <<endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Total 60
上面的類把數(shù)字相加,并返回總和。公有成員 addNum 和 getTotal 是對外的接口,用戶需要知道它們以便使用類。私有成員 total 是對外隱藏的,用戶不需要了解它,但它又是類能正常工作所必需的。
六、c++進階 模板和stl入門
C++ 模板
模板是泛型編程的基礎(chǔ),泛型編程即以一種獨立于任何特定類型的方式編寫代碼。
模板是創(chuàng)建泛型類或函數(shù)的藍圖或公式。庫容器,比如迭代器和算法,都是泛型編程的例子,它們都使用了模板的概念。
每個容器都有一個單一的定義,比如 向量,我們可以定義許多不同類型的向量,比如 vector <int> 或 vector <string>。
您可以使用模板來定義函數(shù)和類,接下來讓我們一起來看看如何使用。
函數(shù)模板
模板函數(shù)定義的一般形式如下所示:
template <class type> ret-type func-name(parameter list) { // 函數(shù)的主體 }
在這里,type 是函數(shù)所使用的數(shù)據(jù)類型的占位符名稱。這個名稱可以在函數(shù)定義中使用。
下面是函數(shù)模板的實例,返回兩個數(shù)中的最大值:
實例
#include <iostream>#include <string> using namespace std; template <typename T>inline T const& Max (T const& a, T const& b) { return a < b ? b:a; } int main (){ int i = 39; int j = 20; cout << 'Max(i, j): ' << Max(i, j) << endl; double f1 = 13.5; double f2 = 20.7; cout << 'Max(f1, f2): ' << Max(f1, f2) << endl; string s1 = 'Hello'; string s2 = 'World'; cout << 'Max(s1, s2): ' << Max(s1, s2) << endl; return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
Max(i, j): 39Max(f1, f2): 20.7Max(s1, s2): World類模板
正如我們定義函數(shù)模板一樣,我們也可以定義類模板。泛型類聲明的一般形式如下所示:
template <class type> class class-name {...}
在這里,type 是占位符類型名稱,可以在類被實例化的時候進行指定。您可以使用一個逗號分隔的列表來定義多個泛型數(shù)據(jù)類型。
下面的實例定義了類 Stack<>,并實現(xiàn)了泛型方法來對元素進行入棧出棧操作:
實例
#include <iostream>#include <vector>#include <cstdlib>#include <string>#include <stdexcept> using namespace std; template <class T>class Stack { private: vector<T> elems; // 元素 public: void push(T const&); // 入棧 void pop(); // 出棧 T top() const; // 返回棧頂元素 bool empty() const{ // 如果為空則返回真。 return elems.empty(); } }; template <class T>void Stack<T>::push (T const& elem) { // 追加傳入元素的副本 elems.push_back(elem); } template <class T>void Stack<T>::pop () { if (elems.empty()) { throw out_of_range('Stack<>::pop(): empty stack'); } // 刪除最后一個元素 elems.pop_back(); } template <class T>T Stack<T>::top () const { if (elems.empty()) { throw out_of_range('Stack<>::top(): empty stack'); } // 返回最后一個元素的副本 return elems.back(); } int main() { try { Stack<int> intStack; // int 類型的棧 Stack<string> stringStack; // string 類型的棧 // 操作 int 類型的棧 intStack.push(7); cout << intStack.top() <<endl; // 操作 string 類型的棧 stringStack.push('hello'); cout << stringStack.top() << std::endl; stringStack.pop(); stringStack.pop(); } catch (exception const& ex) { cerr << 'Exception: ' << ex.what() <<endl; return -1; } }
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
7helloException: Stack<>::pop(): empty stack
C++ STL 入門
上面我們已經(jīng)了解了 C++ 模板的概念。C++ STL(標準模板庫)是一套功能強大的 C++ 模板類,提供了通用的模板類和函數(shù),這些模板類和函數(shù)可以實現(xiàn)多種流行和常用的算法和數(shù)據(jù)結(jié)構(gòu),如向量、鏈表、隊列、棧。
C++ 標準模板庫的核心包括以下三個組件:
組件
描述
容器(Containers)
容器是用來管理某一類對象的集合。C++ 提供了各種不同類型的容器,比如 deque、list、vector、map 等。
算法(Algorithms)
算法作用于容器。它們提供了執(zhí)行各種操作的方式,包括對容器內(nèi)容執(zhí)行初始化、排序、搜索和轉(zhuǎn)換等操作。
迭代器(iterators)
迭代器用于遍歷對象集合的元素。這些集合可能是容器,也可能是容器的子集。
這三個組件都帶有豐富的預(yù)定義函數(shù),幫助我們通過簡單的方式處理復(fù)雜的任務(wù)。
下面的程序演示了向量容器(一個 C++ 標準的模板),它與數(shù)組十分相似,唯一不同的是,向量在需要擴展大小的時候,會自動處理它自己的存儲需求:
實例
#include <iostream>#include <vector>using namespace std; int main(){ // 創(chuàng)建一個向量存儲 int vector<int> vec; int i; // 顯示 vec 的原始大小 cout << 'vector size = ' << vec.size() << endl; // 推入 5 個值到向量中 for(i = 0; i < 5; i++){ vec.push_back(i); } // 顯示 vec 擴展后的大小 cout << 'extended vector size = ' << vec.size() << endl; // 訪問向量中的 5 個值 for(i = 0; i < 5; i++){ cout << 'value of vec [' << i << '] = ' << vec[i] << endl; } // 使用迭代器 iterator 訪問值 vector<int>::iterator v = vec.begin(); while( v != vec.end()) { cout << 'value of v = ' << *v << endl; v++; } return 0;}
當上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
vector size = 0extended vector size = 5value of vec [0] = 0value of vec [1] = 1value of vec [2] = 2value of vec [3] = 3value of vec [4] = 4value of v = 0value of v = 1value of v = 2value of v = 3value of v = 4
關(guān)于上面實例中所使用的各種函數(shù),有幾點要注意:
push_back( ) 成員函數(shù)在向量的末尾插入值,如果有必要會擴展向量的大小。
size( ) 函數(shù)顯示向量的大小。
begin( ) 函數(shù)返回一個指向向量開頭的迭代器。
end( ) 函數(shù)返回一個指向向量末尾的迭代器。