C Primer中文版(第4版)學習筆記_第1頁
C Primer中文版(第4版)學習筆記_第2頁
C Primer中文版(第4版)學習筆記_第3頁
C Primer中文版(第4版)學習筆記_第4頁
C Primer中文版(第4版)學習筆記_第5頁
已閱讀5頁,還剩87頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

編輯版word編輯版word編輯版wordC++Primer中文版(第4版)學習筆記調用GNU或微軟編譯器調用GNU編譯器的默認命令是g++:$g++prog1.cc-oprog1微軟編譯器采用命令cl來調用:C:\directory>cl-GXprog1.cppacmpc2編譯器搭配下面是pc2配置:(以vc為編譯環(huán)境)配置環(huán)境變量:jdk設置:path=C:\ProgramFiles\Java\jdk1.6.0\bin;vc編譯器設置:path=C:\ProgramFiles\MicrosoftVisualStudio\VC98\Bin;lib=C:\ProgramFiles\MicrosoftVisualStudio\VC98\Lib;include=C:\ProgramFiles\MicrosoftVisualStudio\VC98\include環(huán)境變量配置完成之后,設置下pc2就ok了!pc2設置如下:compilecomline:cl.exe{:mainfile}ExecutableFilename:{:basename}.exeprogramexecutioncommandline:{:basename}.exe做到這些配置,基本上編譯就不成問題了!注意,期間可以需要到C:\ProgramFiles\MicrosoftVisualStudio\COMMON\Tools路徑下拷貝mspdb60.dll到C:\ProgramFiles\MicrosoftVisualStudio\VC98\Bin;!這個自己調試就Ok了!訪問main函數的返回值的方式和系統(tǒng)有關。不論UNIX還是Windows系統(tǒng),執(zhí)行程序后,必須發(fā)出一個適當的echo命令。UNIX系統(tǒng)中,通過鍵入如下命令獲取狀態(tài):$echo$?要在Windows系統(tǒng)下查看狀態(tài),鍵入C:\directory>echo%ERRORLEVEL%再談編譯編譯器的部分工作是尋找程序代碼中的錯誤。編譯器不能查出程序的意義是否正確,但它可以查出程序形式上的錯誤。下面是編譯器能查出的最普遍的一些錯誤。1、語法錯誤。程序員犯了C++語言中的語法錯誤。下面代碼段說明常見的語法錯誤;每個注釋描述下一行的錯誤。//error:missing')'inparameterlistformainintmain({//error:usedcolon,notasemicolonafterendlstd::cout<<"Readeachfile."<<std::endl://error:missingquotesaroundstringliteralstd::cout<<Updatemaster.<<std::endl;//ok:noerrorsonthislinestd::cout<<"Writenewmaster."<<std::endl;//error:missing';'onreturnstatementreturn0}2、類型錯誤。C++中每個數據項都有其相關聯的類型。例如,值10是一個整數。用雙引號標注起來的單詞“hello”是字符串字面值。類型錯誤的一個實例是傳遞了字符串字面值給應該得到整型參數的函數。3、聲明錯誤。C++程序中使用的每個名字必須在使用之前聲明。沒有聲明名字通常會導致錯誤信息。最常見的兩種聲明錯誤,是從標準庫中訪問名字時忘記使用“std::”,以及由于疏忽而拼錯標識符名:#include<iostream>intmain(){intv1,v2;std::cin>>v>>v2;//error:uses"v"not"v1"http://coutnotdefined,shouldbestd::coutcout<<v1+v2<<std::endl;return0;}錯誤信息包含行號和編譯器對我們所犯錯誤的簡要描述。按錯誤報告的順序改正錯誤是個好習慣,通常一個錯誤可能會產生一連串的影響,并導致編譯器報告比實際多得多的錯誤。最好在每次修改后或最多改正了一些顯而易見的錯誤后,就重新編譯代碼。這個循環(huán)就是眾所周知的編輯—編譯—調試。從鍵盤輸入文件結束符操作系統(tǒng)使用不同的值作為文件結束符。Windows系統(tǒng)下我們通過鍵入control—z——同時鍵入“ctrl”鍵和“z”鍵,來輸入文件結束符。Unix系統(tǒng)中,包括MacOS—X機器,通常用control—d。標準庫的頭文件用尖括號<>括起來,非標準庫的頭文件用雙引號""括起來。我們能將值20定義成下列三種形式中的任意一種:20//decimal024//octal0x14//hexadecimal以0(零)開頭的字面值整數常量表示八進制,以0x或0X開頭的表示十六進制。定義長整型時,應該使用大寫字母L。小寫字母l很容易和數值1混淆。類似地,可通過在數值后面加U或u定義unsigned類型。使用科學計數法時,指數用E或者e表示。默認的浮點字面值常量為double類型。在數值的后面加上F或f表示單精度。同樣加上L或者l表示擴展精度非打印字符的轉義序列newline換行符\nhorizontaltab水平制表符\tverticaltab縱向制表符\vbackspace退格符\bcarriagereturn回車符\rformfeed進紙符\falert(bell)報警(響鈴)符\abackslash反斜線\\questionmark疑問號\?singlequote單引號\'doublequote雙引號\"我們可以將任何字符表示為以下形式的通用轉義字符:\ooo這里ooo表示三個八進制數字,這三個數字表示字符的數字值。下面的例子是用ASCII碼字符集表示字面值常量:\7(bell)\12(newline)\40(blank)\0(null)\062('2')\115('M')字符’\0’通常表示“空字符(nullcharacter)”,我們將會看到它有著非常特殊的意義。同樣也可以用十六進制轉義字符來定義字符:\xdddC++的格式非常自由。特別是有一些地方不能插入空格,其中之一是在單詞中間。特別是不能在單詞中間斷開一行。但可以通過使用反斜線符號巧妙實現://ok:A\beforeanewlineignoresthelinebreakstd::cou\t<<"Hi"<<st\d::endl;等價于std::cout<<"Hi"<<std::endl;可以使用這個特性來編寫長字符串字面值://multilinestringliteralstd::cout<<"amulti-line\stringliteral\usingabackslash"<<std::endl;return0;}注意反斜線符號必須是該行的尾字符——不允許有注釋或空格符。同樣,后繼行行首的任何空格和制表符都是字符串字面值的一部分。正因如此,長字符串字面值的后繼行才不會有正常的縮進。C++關鍵字asmdoifreturntryautodoubleinlineshorttypedefbooldynamic_castintsignedtypeidbreakelselongsizeoftypenamecaseenummutablestaticunioncatchexplicitnamespacestatic_castunsignedcharexportnewstructusingclassexternoperatorswitchvirtualconstfalseprivatetemplatevoidconst_castfloatprotectedthisvolatilecontinueforpublicthrowwchar_tdefaultfriendregistertruewhiledeletegotoreinterpret_cast

C++操作符替代名andbitandcomplnot_eqor_eqxor_eqand_eqbitornotorxor

C++支持兩種初始化變量的形式:復制初始化和直接初始化。復制初始化語法用等號(=),直接初始化則是把初始化式放在括號中:intival(1024);//direct-initializationintival=1024;//copy-initialization初始化內置類型的對象只有一種方法:提供一個值,并且把這個值復制到新定義的對象中。對內置類型來說,復制初始化和直接初始化幾乎沒有差別。對類類型的對象來說,有些初始化僅能用直接初始化完成。要想理解其中緣由,需要初步了解類是如何控制初始化的。變量初始化規(guī)則內置類型變量的初始化使用未初始化的變量是常見的程序錯誤,通常也是難以發(fā)現的錯誤。雖然許多編譯器都至少會提醒不要使用未初始化變量,但是編譯器并未被要求去檢測未初始化變量的使用。而且,沒有一個編譯器能檢測出所有未初始化變量的使用。有時我們很幸運,使用未初始化的變量導致程序在運行時突然崩潰。一旦跟蹤到程序崩潰的位置,就可以輕易地發(fā)現沒有正確地初始化變量。但有時,程序運行完畢卻產生錯誤的結果。更糟糕的是,程序運行在一部機器上時能產生正確的結果,但在另外一部機器上卻不能得到正確的結果。添加代碼到程序的一些不相關的位置,會導致我們認為是正確的程序產生錯誤的結果。建議每個內置類型的對象都要初始化。雖然這樣做并不總是必需的,但是會更加容易和安全,除非你確定忽略初始化式不會帶來風險。類類型變量的初始化每個類都定義了該類型的對象可以怎樣初始化。類通過定義一個或多個構造函數來控制類對象的初始化。如果定義某個類的變量時沒有提供初始化式,這個類也可以定義初始化時的操作。它是通過定義一個特殊的構造函數即默認構造函數來實現的。這個構造函數之所以被稱作默認構造函數,是因為它是“默認”運行的。如果沒有提供初始化式,那么就會使用默認構造函數。不管變量在哪里定義,默認構造函數都會被使用。變量的聲明和定義變量的定義用于為變量分配存儲空間,還可以為變量指定初始值。在一個程序中,變量有且僅有一個定義。聲明用于向程序表明變量的類型和名字。定義也是聲明:當定義變量時我們聲明了它的類型和名字。可以通過使用extern關鍵字聲明變量名而不定義它。不定義變量的聲明包括對象名、對象類型和對象類型前的關鍵字extern:externinti;//declaresbutdoesnotdefineiinti;//declaresanddefinesiextern聲明不是定義,也不分配存儲空間。事實上,它只是說明變量定義在程序的其他地方。程序中變量可以聲明多次,但只能定義一次。只有當聲明也是定義時,聲明才可以有初始化式,因為只有定義才分配存儲空間。初始化式必須要有存儲空間來進行初始化。如果聲明有初始化式,那么它可被當作是定義,即使聲明標記為extern:externdoublepi=3.1416;//definition雖然使用了extern,但是這條語句還是定義了pi,分配并初始化了存儲空間。只有當extern聲明位于函數外部時,才可以含有初始化式。因為已初始化的extern聲明被當作是定義,所以該變量任何隨后的定義都是錯誤的:externdoublepi=3.1416;//definitiondoublepi;//error:redefinitionofpi同樣,隨后的含有初始化式的extern聲明也是錯誤的:externdoublepi=3.1416;//definitionexterndoublepi;//ok:declarationnotdefinitionexterndoublepi=3.1416;//error:redefinitionofpi聲明和定義之間的區(qū)別可能看起來微不足道,但事實上卻是舉足輕重的。在C++語言中,變量必須且僅能定義一次,而且在使用變量之前必須定義或聲明變量。任何在多個文件中使用的變量都需要有與定義分離的聲明。在這種情況下,一個文件含有變量的定義,使用該變量的其他文件則包含該變量的聲明(而不是定義)。下列程序段將會輸出什么?inti=100,sum=0;for(inti=0;i!=10;++i)sum+=i;std::cout<<i<<""<<sum<<std::endl;【解答】輸出為:10045for語句中定義的變量i,其作用域僅限于for語句內部。輸出的i值是for語句之前所定義的變量i的值。const因為常量在定義后就不能被修改,所以定義時必須初始化:conststd::stringhi="hello!";//ok:initializedconstinti,j=0;//error:iisuninitializedconst引用引用必須用與該引用同類型的對象初始化:intival=1024;int&refVal=ival;//ok:refValreferstoivalint&refVal2;//error:areferencemustbeinitializedint&refVal3=10;//error:initializermustbeanobject因為引用只是它綁定的對象的另一名字,作用在引用上的所有操作事實上都是作用在該引用綁定的對象上:const引用const引用是指向const對象的引用:constintival=1024;constint&refVal=ival;//ok:bothreferenceandobjectareconstint&ref2=ival;//error:nonconstreferencetoaconstobject可以讀取但不能修改refVal,因此,任何對refVal的賦值都是不合法的。這個限制有其意義:不能直接對ival賦值,因此不能通過使用refVal來修改ival。類定義使用struct關鍵字C++支持另一個關鍵字struct,它也可以定義類類型。struct關鍵字是從C語言中繼承過來的。用class和struct關鍵字定義類的唯一差別在于默認訪問級別:默認情況下,struct的成員為public,而class的成員為private。classSales_item{public://operationsonSales_itemobjectswillgohereprivate:std::stringisbn;unsignedunits_sold;doublerevenue;};structSales_item{//noneedforpubliclabel,membersarepublicbydefault//operationsonSales_itemobjectsprivate:std::stringisbn;unsignedunits_sold;doublerevenue;};編譯和鏈接多個源文件我們可以按以下方式編譯這兩個文件:$CC-cmain.ccSales_item.cc#bydefaultgeneratesa.exe#somecompilersgeneratea.out#putstheexecutableinmain.exe$CC-cmain.ccSales_item.cc-omain其中$是我們的系統(tǒng)提示符,#開始命令行注釋?,F在我們可以運行可執(zhí)行文件,它將運行我們的main程序。如果我們只是修改了一個.cc源文件,較有效的方法是只重新編譯修改過的文件。大多數編譯器都提供了分別編譯每一個文件的方法。通常這個過程產生.o文件,.o擴展名暗示該文件含有目標代碼。編譯器允許我們把目標文件鏈接在一起以形成可執(zhí)行文件。我們所使用的系統(tǒng)可以通過命令名CC調用編譯。因此可以按以下方式編譯程序:$CC-cmain.cc#generatesmain.o$CC-cSales_item.cc#generatesSales_item.o$CCmain.oSales_item.o#bydefaultgeneratesa.exe;#somecompilersgeneratea.out#putstheexecutableinmain.exe$CCmain.oSales_item.o-omain頭文件用于聲明而不是用于定義因為頭文件包含在多個源文件中,所以不應該含有變量或函數的定義。對于頭文件不應該含有定義這一規(guī)則,有三個例外。頭文件可以定義類、值在編譯時就已知道的const對象和inline函數。這些實體可在多個源文件中定義,只要每個源文件中的定義是相同的。一些const對象定義在頭文件中當該const變量是用常量表達式初始化時,可以保證所有的變量都有相同的值。但是在實踐中,大部分的編譯器在編譯時都會用相應的常量表達式替換這些const變量的任何使用。所以,在實踐中不會有任何存儲空間用于存儲用常量表達式初始化的const變量??稍陬^文件中定義。如果const變量不是用常量表達式初始化,那么它就不應該在頭文件中定義。相反,和其他的變量一樣,該const變量應該在一個源文件中定義并初始化。應在頭文件中為它添加extern聲明,以使其能被多個文件共享。下列聲明和定義哪些應該放在頭文件中?哪些應該放在源文件中?請解釋原因。(a)intvar;(b)constdoublepi=3.1416;(c)externinttotal=255;(d)constdoublesq2=squt(2.0);【解答】(a)、(c)、(d)應放在源文件中,因為(a)和(c)是變量定義,定義通常應放在源文件中。(d)中的const變量sq2不是用常量表達式初始化的,所以也應該放在源文件中。(b)中的const變量pi是用常量表達式初始化的,應該放在頭文件中。不使用命名空間#include<iostream>intmain(){intsum=0,val=1;//keepexecutingthewhileuntilvalisgreaterthan10while(val<=10){sum+=val;//assignssum+valtosum++val;//add1toval}std::cout<<"Sumof1to10inclusiveis"<<sum<<std::endl;return0;}命名空間的

using

聲明using

聲明可以在不需要加前綴

namespace_name::

的情況下訪問命名空間中的名字。using

聲明的形式如下:usingnamespace::name;一旦使用了

using

聲明,我們就可以直接引用名字,而不需要再引用該名字的命名空間。#include<string>#include<iostream>//usingdeclarationsstatesourintenttousethesenamesfromthenamespacestdusingstd::cin;usingstd::string;intmain(){strings;//ok:stringisnowasynonymforstd::stringcin>>s;//ok:cinisnowasynonymforstd::cincout<<s;//error:nousingdeclaration;wemustusefullnamestd::cout<<s;//ok:explicitlyusecoutfromnamepsacestd}using指示usingnamespacestd;標準庫

string

類型幾種初始化

string

對象的方式strings1;默認構造函數

s1

為空串strings2(s1);將

s2

初始化為

s1

的一個副本strings3("value");將

s3

初始化為一個字符串字面值副本strings4(n,'c');將

s4

初始化為字符

'c'

n

個副本標準庫

string

類型和字符串字面值因為歷史原因以及為了與

C

語言兼容,字符串字面值與標準庫

string

類型不是同一種類型。這一點很容易引起混亂,編程時一定要注意區(qū)分字符串字面值和

string

數據類型的使用,這很重要。//Note:#includeandusingdeclarationsmustbeaddedtocompilethiscodeintmain(){strings;//emptystringcin>>s;//readwhitespace-separatedstringintoscout<<s<<endl;//writestotheoutputreturn0;}以上程序首先定義命名為

s

string

第二行代碼:cin>>s;//readwhitespace-separatedstringintos從標準輸入讀取

string

并將讀入的串存儲在

s

中。string

類型的輸入操作符:讀取并忽略開頭所有的空白字符(如空格,換行符,制表符)。讀取字符直至再次遇到空白字符,讀取終止。讀入未知數目的

string

對象和內置類型的輸入操作一樣,string

的輸入操作符也會返回所讀的數據流。因此,可以把輸入操作作為判斷條件,這與我們在

1.4.4

節(jié)讀取整型數據的程序做法是一樣的。下面的程序將從標準輸入讀取一組

string

對象,然后在標準輸出上逐行輸出:intmain(){stringword;//readuntilend-of-file,writingeachwordtoanewlinewhile(cin>>word)cout<<word<<endl;return0;}使用

getline

讀取整行文本getline。這個函數接受兩個參數:一個輸入流對象和一個

string

對象。getline

函數從輸入流的下一行讀取,并保存讀取的內容到不包括換行符。和輸入操作符不一樣的是,getline

并不忽略行開頭的換行符。只要

getline

遇到換行符,即便它是輸入的第一個字符,getline

也將停止讀入并返回。如果第一個字符就是換行符,則

string

參數將被置為空

string。getline

函數將

istream

參數作為返回值,和輸入操作符一樣也把它用作判斷條件。例如,重寫前面那段程序,把每行輸出一個單詞改為每次輸出一行文本:intmain(){stringline;//readlineattimeuntilend-of-filewhile(getline(cin,line))cout<<line<<endl;return0;}由于

getline

函數返回時丟棄換行符,換行符將不會存儲在

string

對象中。string

對象的操作.empty()如果

s

為空串,則返回

true,否則返回

false。s.size()返回

s

中字符的個數s[n]返回

s

中位置為

n

的字符,位置從

0

開始計數s1+s2把

s1

和s2

連接成一個新字符串,返回新生成的字符串s1=s2把

s1

內容替換為

s2

的副本v1==v2比較

v1

v2的內容,相等則返回

true,否則返回

false!=,<,<=,>,and>=保持這些操作符慣有的含義string::size_type

類型從邏輯上來講,size()

成員函數似乎應該返回整形數值,或如

2.2

節(jié)“建議”中所述的無符號整數。但事實上,size

操作返回的是

string::size_type

類型的值。我們需要對這種類型做一些解釋。任何存儲

string

size

操作結果的變量必須為

string::size_type

類型。特別重要的是,不要把

size

的返回值賦給一個

int

變量。和字符串字面值的連接當進行

string

對象和字符串字面值混合連接操作時,+

操作符的左右操作數必須至少有一個是

string

類型的:strings1="hello";//nopunctuationstrings2="world";strings3=s1+",";//ok:addingastringandaliteralstrings4="hello"+",";//error:nostringoperandstrings5=s1+","+"world";//ok:each+hasstringoperandstrings6="hello"+","+s2;//error:can'taddstringliterals可用下標操作符分別取出

string

對象的每個字符,分行輸出:stringstr("somestring");for(string::size_typeix=0;ix!=str.size();++ix)cout<<str[ix]<<endl;cctypeFunctionsisalnum(c)如果

c

是字母或數字,則為

True。isalpha(c)如果

c

是字母,則為

true。iscntrl(c)如果

c

是控制字符,則為

true

isdigit(c)如果

c

是數字,則為

true。isgraph(c)如果

c

不是空格,但可打印,則為

true。islower(c)如果

c

是小寫字母,則為

true。isprint(c)如果

c

是可打印的字符,則為

true。ispunct(c)如果

c

是標點符號,則

true。isspace(c)如果

c

是空白字符,則為

true。isupper(c)如果

c

是大寫字母,則

true。isxdigit(c)如果是

c

十六進制數,則為

true。tolower(c)如果

c

大寫字母,返回其小寫字母形式,否則直接返回

c。toupper(c)如果

c

是小寫字母,則返回其大寫字母形式,否則直接返回

c。建議:采用

C

標準庫頭文件的

C++

版本C++

標準庫除了定義了一些選定于

C++

的設施外,還包括

C

標準庫。C++

中的頭文件

cctype

其實就是利用了

C

標準庫函數,這些庫函數就定義在

C

標準庫的

ctype.h

頭文件中。C

標準庫頭文件命名形式為

name

C++

版本則命名為

cname

,少了后綴,.h

而在頭文件名前加了

c

表示這個頭文件源自

C

標準庫。因此,cctype

ctype.h

文件的內容是一樣的,只是采用了更適合

C++程序的形式。特別地,cname

頭文件中定義的名字都定義在命名空間

std

內,而

.h

版本中的名字卻不是這樣。通常,C++

程序中應采用

cname

這種頭文件的版本,而不采用

name.h

版本,這樣,標準庫中的名字在命名空間

std

中保持一致。使用

.h

版本會給程序員帶來負擔,因為他們必須記得哪些標準庫名字是從

C

繼承來的,而哪些是

C++

所特有的。標準庫vector類型vector是同一種類型的對象的集合,每個對象都有一個對應的整數索引值。和string對象一樣,標準庫將負責管理與存儲元素相關的內存。我們把vector稱為容器,是因為它可以包含其他對象。一個容器中的所有對象都必須是同一種類型的。vector是一個類模板(classtemplate)。使用模板可以編寫一個類定義或函數定義,而用于多個不同的數據類型。因此,我們可以定義保存string對象的vector,或保存int值的vector,又或是保存自定義的類類型對象(如Sales_items對象)的vector。聲明從類模板產生的某種類型的對象,需要提供附加信息,信息的種類取決于模板。以vector為例,必須說明vector保存何種對象的類型,通過將類型放在類型放在類模板名稱后面的尖括號中來指定類型:vector<int>ivec;//ivecholdsobjectsoftypeintvector<Sales_item>Sales_vec;//holdsSales_itemsvector不是一種數據類型,而只是一個類模板,可用來定義任意多種數據類型。vector類型的每一種都指定了其保存元素的類型。因此,vector<int>和vector<string>都是數據類型。vector對象的定義和初始化vector<T>v1;vector保存類型為T對象。默認構造函數v1為空。vector<T>v2(v1);v2是v1的一個副本。vector<T>v3(n,i);v3包含n個值為i的元素。vector<T>v4(n);v4含有值初始化的元素的n個副本。vector對象動態(tài)增長vector對象(以及其他標準庫容器對象)的重要屬性就在于可以在運行時高效地添加元素。因為vector增長的效率高,在元素值已知的情況下,最好是動態(tài)地添加元素。雖然可以對給定元素個數的vector對象預先分配內存,但更有效的方法是先初始化一個空vector對象,然后再動態(tài)地增加元素(我們隨后將學習如何進行這樣的操作)。值初始化如果沒有指定元素的初始化式,那么標準庫將自行提供一個元素初始值進行值初始化(valueinitializationd)。這個由庫生成的初始值將用來初始化容器中的每個元素,具體值為何,取決于存儲在vector中元素的數據類型。如果vector保存內置類型(如int類型)的元素,那么標準庫將用0值創(chuàng)建元素初始化式:vector<string>fvec(10);//10elements,eachinitializedto0如果vector保存的是含有構造函數的類類型(如string)的元素,標準庫將用該類型的默認構造函數創(chuàng)建元素初始化式:vector<string>svec(10);//10elements,eachanemptystringvectorOperationsv.empty()如果v為空,則返回true,否則返回false。v.size()返回v中元素的個數。v.push_back(t)在v的末尾增加一個值為t的元素。v[n]返回v中位置為n的元素。v1=v2把v1的元素替換為v2中元素的副本。v1==v2如果v1與v2相等,則返回true。!=,<,<=,

>,and>=保持這些操作符慣有的含義。vector對象的size使用size_type類型時,必須指出該類型是在哪里定義的。vector類型總是包括總是包括vector的元素類型:vector<int>::size_type//okvector::size_type//error初學C++的程序員可能會認為vector的下標操作可以添加元素,其實不然:vector<int>ivec;//emptyvectorfor(vector<int>::size_typeix=0;ix!=10;++ix)ivec[ix]=ix;//disaster:ivechasnoelements上述程序試圖在ivec中插入10個新元素,元素值依次為0到9的整數。但是,這里ivec是空的vector對象,而且下標只能用于獲取已存在的元素。這個循環(huán)的正確寫法應該是:for(vector<int>::size_typeix=0;ix!=10;++ix)ivec.push_back(ix);//ok:addsnewelementwithvalueix必須是已存在的元素才能用下標操作符進行索引。通過下標操作進行賦值時,不會添加任何元素。警告:僅能對確知已存在的元素進行下標操作迭代器簡介除了使用下標來訪問

vector

對象的元素外,標準庫還提供了另一種訪問元素的方法:使用迭代器(iterator)。迭代器是一種檢查容器內元素并遍歷元素的數據類型。標準庫為每一種標準容器(包括

vector)定義了一種迭代器類型。迭代器類型提供了比下標操作更通用化的方法:所有的標準庫容器都定義了相應的迭代器類型,而只有少數的容器支持下標操作。因為迭代器對所有的容器都適用,現代

C++

程序更傾向于使用迭代器而不是下標操作訪問容器元素,即使對支持下標操作的

vector

類型也是這樣。每種容器類型都定義了自己的迭代器類型,如

vector:vector<int>::iteratoriter;begin

end

操作每種容器都定義了一對命名為

begin

end

的函數,用于返回迭代器。如果容器中有元素的話,由

begin

返回的迭代器指向第一個元素:vector<int>::iteratoriter=ivec.begin();由

end

操作返回的迭代器指向

vector

的“末端元素的下一個”?!俺瞿┒说鳌保╫ff-the-enditerator)。表明它指向了一個不存在的元素。如果

vector

為空,begin

返回的迭代器與

end

返回的迭代器相同。由于

end

操作返回的迭代器不指向任何元素,因此不能對它進行解引用(*)或自增操作。for(vector<int>::iteratoriter=ivec.begin();iter!=ivec.end();++iter)*iter=0;//setelementtowhichiterrefersto0const_iterator每種容器類型還定義了一種名為

const_iterator

的類型,該類型只能用于讀取容器內元素,但不能改變其值。for(vector<string>::const_iteratoriter=text.begin();iter!=text.end();++iter)*iter="";//error:*iterisconst使用

const_iterator

類型時,我們可以得到一個迭代器,它自身的值可以改變,但不能用來改變其所指向的元素的值??梢詫Φ鬟M行自增以及使用解引用操作符來讀取值,但不能對該元素賦值。不要把

const_iterator

對象與

const

iterator

對象混淆起來。聲明一個

const

迭代器時,必須初始化迭代器。一旦被初始化后,就不能改變它的值:vector<int>nums(10);//numsisnonconstconstvector<int>::iteratorcit=nums.begin();*cit=1;//ok:citcanchangeitsunderlyingelement++cit;//error:can'tchangethevalueofcit任何改變

vector

長度的操作都會使已存在的迭代器失效。例如,在調用

push_back

之后,就不能再信賴指向

vector

的迭代器的值了。標準庫

bitset

初始化

bitset

對象的方法bitset<n>b;b

n

位,每位都

0bitset<n>b(u);b

unsignedlong

u

的一個副本bitset<n>b(s);b

string

對象

s

中含有的位串的副本bitset<n>b(s,pos,n);b

s

中從位置

pos

開始的n

個位的副本。bitset<32>bitvec;//32bits,allzero這條語句把

bitvec

定義為含有

32

個位的

bitset

對象。和

vector

的元素一樣,bitset

中的位是沒有命名的,程序員只能按位置來訪問。位集合的位置編號從

0

開始,因此,bitvec

的位序是從

0

31。以

0

位開始的位串是低階位(low-order),以

31

位結束的位串是高階位(high-order)。string

對象和

bitsets

對象之間是反向轉化的:string

對象的最右邊字符(即下標最大的那個字符)用來初始化

bitset

對象的低階位(即下標為

0

的位)。當用

string

對象初始化

bitset

對象時,記住這一差別很重要。bitset

操作b.any()b

中是否存在置為

1

的二進制位?b.none()b

中不存在置為

1

的二進制位?b.count()b

中置為

1

的二進制位的個數b.size()b

中二進制位的個數b[pos]訪問

b

中在

pos

處二進制位b.test(pos)b

中在

pos

處的二進制位置是否為

1b.set()把

b

中所有二進制位都置為

1b.set(pos)把

b

中在

pos

處的二進制位置為

1b.reset()把

b

中所有二進制位都置為

0b.reset(pos)把

b

中在

pos

處的二進制位置為

0b.flip()把

b

中所有二進制位逐位取反b.flip(pos)把

b

中在

pos

處的二進制位取反b.to_ulong()用

b

中同樣的二進制位返回一個

unsignedlong

值os<<b把

b

中的位集輸出到

os

流size_t

類型定義在

cstddef

頭文件中,該文件是

C

標準庫的頭文件

stddef.h

C++

版本。它是一個與機器相關的

unsigned

類型,其大小足以保證存儲內在中對象的大小。建議:盡量避免使用指針和數組指針和數組容易產生不可預料的錯誤。其中一部分是概念上的問題:指針用于低級操作,容易產生與繁瑣細節(jié)相關的(bookkeeping)錯誤。其他錯誤則源于使用指針的語法規(guī)則,特別是聲明指針的語法。許多有用的程序都可不使用數組或指針實現,現代C++程序采用vector類型和迭代器取代一般的數組、采用string類型取代C風格字符串。一個有效的指針必然是以下三種狀態(tài)之一:保存一個特定對象的地址;指向某個對象后面的另一對象;或者是0值。若指針保存0值,表明它不指向任何對象。未初始化的指針是無效的,直到給該指針賦值后,才可使用它。下列定義和賦值都是合法的:intival=1024;int*pi=0;//piinitializedtoaddressnoobjectint*pi2=&ival;//pi2initializedtoaddressofivalint*pi3;//ok,butdangerous,pi3isuninitializedpi=pi2;//piandpi2addressthesameobject,e.g.ivalpi2=0;//pi2nowaddressesnoobject避免使用未初始化的指針很多運行時錯誤都源于使用了未初始化的指針。指針初始化和賦值操作的約束對指針進行初始化或賦值只能使用以下四種類型的值:0值常量表達式,例如,在編譯時可獲得0值的整型const對象或字面值常量0。類型匹配的對象的地址。另一對象末的下一地址。同類型的另一個有效指針。把int型變量賦給指針是非法的,盡管此int型變量的值可能為0。但允許把數值0或在編譯時可獲得0值的const量賦給指針:intival;intzero=0;constintc_ival=0;int*pi=ival;//error:piinitializedfromintvalueofivalpi=zero;//error:piassignedintvalueofzeropi=c_ival;//ok:c_ivalisaconstwithcompile-timevalueof0pi=0;//ok:directlyinitializetoliteralconstant0void*指針C++提供了一種特殊的指針類型void*,它可以保存任何類型對象的地址:void*表明該指針與一地址值相關,但不清楚存儲在此地址上的對象的類型。void*指針只支持幾種有限的操作:與另一個指針進行比較;向函數傳遞void*指針或從函數返回void*指針;給另一個void*指針賦值。不允許使用void*指針操縱它所指向的對象。永遠不要忘記字符串結束符null如果兩個操作數為正,除法(/)和求模(%)操作的結果也是正數(或零);如果兩個操作數都是負數,除法操作的結果為正數(或零),而求模操作的結果則為負數(或零);如果只有一個操作數為負數,這兩種操作的結果取決于機器;求模結果的符號也取決于機器,而除法操作的值則是負數(或零):21%6;//ok:resultis321%7;//ok:resultis0-21%-8;//ok:resultis-521%-5;//machine-dependent:resultis1or-421/6;//ok:resultis321/7;//ok:resultis3-21/-8;//ok:resultis221/-5;//machine-dependent:result-4or-5當只有一個操作數為負數時,求模操作結果值的符號可依據分子(被除數)或分母(除數)的符號而定。如果求模的結果隨分子的符號,則除出來的值向零一側取整;如果求模與分母的符號匹配,則除出來的值向負無窮一側取整。邏輯與、邏輯或操作符邏輯與和邏輯或操作符總是先計算其左操作數,然后再計算其右操作數。只有在僅靠左操作數的值無法確定該邏輯表達式的結果時,才會求解其右操作數。我們常常稱這種求值策略為“短路求值(short-circuitevaluation)”。對于位操作符,由于系統(tǒng)不能確保如何處理其操作數的符號位,所以強烈建議使用unsigned整型操作數。int*pi=newint;//pipointstoanuninitializedintint*pi=newint();//pipointstoanintvalue-initializedto0第一個語句的int型變量沒有初始化,而第二個語句的int型變量則被初始化為0。利用const引用避免復制編寫一個比較兩個string對象長度的函數作為例子。這個函數需要訪問每個string對象的size,但不必修改這些對象。由于string對象可能相當長,所以我們希望避免復制操作。使用const引用就可避免復制://comparethelengthoftwostringsboolisShorter(conststring&s1,conststring&s2){returns1.size()<s2.size();}如果使用引用形參的唯一目的是避免復制實參,則應將形參定義為const引用。vector和其他容器類型的形參通常,函數不應該有vector或其他標準庫容器類型的形參。調用含有普通的非引用vector形參的函數將會復制vector的每一個元素。事實上,C++程序員傾向于通過傳遞指向容器中需要處理的元素的迭代器來傳遞容器://passiteratorstothefirstandonepastthelastelementtoprintvoidprint(vector<int>::const_iteratorbeg,vector<int>::const_iteratorend){while(beg!=end){cout<<*beg++;if(beg!=end)cout<<"";//nospaceafterlastelement}cout<<endl;}通過引用傳遞數組和其他類型一樣,數組形參可聲明為數組的引用。如果形參是數組的引用,編譯器不會將數組實參轉化為指針,而是傳遞數組的引用本身。在這種情況下,數組大小成為形參和實參類型的一部分。編譯器檢查數組的實參的大小與形參的大小是否匹配://ok:parameterisareferencetoanarray;sizeofarrayisfixedvoidprintValues(int(&arr)[10]){/*...*/}intmain(){inti=0,j[2]={0,1};intk[10]={0,1,2,3,4,5,6,7,8,9};printValues(&i);//error:argumentisnotanarrayof10intsprintValues(j);//error:argumentisnotanarrayof10intsprintValues(k);//ok:argumentisanarrayof10intsreturn0;}這個版本的printValues函數只嚴格地接受含有10個int型數值的數組,這限制了哪些數組可以傳遞。然而,由于形參是引用,在函數體中依賴數組的大小是安全的://ok:parameterisareferencetoanarray;sizeofarrayisfixedvoidprintValues(int(&arr)[10]){for(size_ti=0;i!=10;++i){cout<<arr[i]<<endl;}}&arr兩邊的圓括號是必需的,因為下標操作符具有更高的優(yōu)先級:main:處理命令行選項intmain(intargc,char*argv[]){...}第二個形參argv是一個C風格字符串數組。第一個形參argc則用于傳遞該數組中字符串的個數。由于第二個參數是一個數組,主函數main也可以這樣定義:intmain(intargc,char**argv){...}prog-d-oofiledata0當將實參傳遞給主函數main時,argv中的第一個字符串(如果有的話)通常是程序的名字。接下來的元素將額外的可選字符串傳遞給主函數main。以前面的命令行為例,argc應設為5,argv會保存下面幾個C風格字符串:argv[0]="prog";argv[1]="-d";argv[2]="-o";argv[3]="ofile";argv[4]="data0";靜態(tài)局部對象一個變量如果位于函數的作用域內,但生命期跨越了這個函數的多次調用,這種變量往往很有用。則應該將這樣的對象定義為static(靜態(tài)的)。static局部對象確保不遲于在程序執(zhí)行流程第一次經過該對象的定義語句時進行初始化。這種對象一旦被創(chuàng)建,在程序結束前都不會撤銷。當定義靜態(tài)局部對象的函數結束時,靜態(tài)局部對象不會撤銷。在該函數被多次調用的過程中,靜態(tài)局部對象會持續(xù)存在并保持它的值。考慮下面的小例子,這個函數計算了自己被調用的次數:size_tcount_calls(){staticsize_tctr=0;//valuewillpersistacrosscallsreturn++ctr;}intmain(){for(size_ti=0;i!=10;++i)cout<<count_calls()<<endl;return0;}這個程序會依次輸出1到10(包含10)的整數。在第一次調用函數count_calls之前,ctr就已創(chuàng)建并賦予初值0。每次函數調用都使加1,并且返回其當前值。在執(zhí)行函數count_calls時,變量ctr就已經存在并且保留上次調用該函數時的值。因此,第二次調用時,ctr的值為1,第三次為2,依此類推。重載與作用域voidprint(conststring&);voidprint(double);//overloadstheprintfunctionvoidfooBar(intival){voidprint(int);//newscope:hidespreviousinstancesofprintprint("Value:");//error:print(conststring&)ishiddenprint(ival);//ok:print(int)isvisibleprint(3.14);//ok:callsprint(int);print(double)ishidden}函數fooBar中的print(int)聲明將屏蔽print的其他聲明,就像只有一個有效的print函數一樣:該函數僅帶有一個int型形參。在這個作用域或嵌套在這個作用域里的其他作用域中,名字print的任何使用都將解釋為這個print函數實例。調用print時,編譯器首先檢索這個名字的聲明,找到只有一個int型形參的print函數的局部聲明。一旦找到這個名字,編譯器將不再繼續(xù)檢查這個名字是否在外層作用域中存在,即編譯器將認同找到的這個聲明即是程序需要調用的函數,余下的工作只是檢查該名字的使用是否有效。指向函數的指針函數指針是指指向函數而非指向對象的指針。像其他指針一樣,函數指針也指向某個特定的類型。函數類型由其返回類型以及形參表確定,而與函數名無關://pfpointstofunctionreturningboolthattakestwoconststringreferencesbool(*pf)(conststring&,conststring&);這個語句將pf聲明為指向函數的指針,它所指向的函數帶有兩個conststring&類型的形參和bool類型的返回值。用typedef簡化函數指針的定義typedefbool(*cmpFcn)(conststring&,conststring&);該定義表示cmpFcn是一種指向函數的指針類型的名字。該指針類型為“指向返回bool類型并帶有兩個conststring引用形參的函數的指針”。在要使用這種函數指針類型時,只需直接使用cmpFcn即可,不必每次都把整個類型聲明全部寫出來。bool(*)(conststring&,conststring&);可使用函數名對函數指針做初始化或賦值:cmpFcnpf1=0;//ok:unboundpointertofunctioncmpFcnpf2=lengthCompare;//ok:pointertypematchesfunction'stypepf1=lengthCompare;//ok:pointertypematchesfunction'stypepf2=pf1;//ok:pointertypesmatch返回指向函數的指針int(*ff(int))(int*,int);閱讀函數指針聲明的最佳方法是從聲明的名字開始由里而外理解。要理解該聲明的含義,首先觀察:ff(int)將ff聲明為一個函數,它帶有一個int型的形參。該函數返回int(*)(int*,int);它是一個指向函數的指針,所指向的函數返回int型并帶有兩個分別是int*型和int型的形參。使用typedef可使該定義更簡明易懂://PFisapointertoafunctionreturninganint,takinganint*andaninttypedefint(*PF)(int*,int);PFff(int);//ffreturnsapointertofunction允許將形參定義為函數類型,但函數的返回類型則必須是指向函數的指針,而不能是函數。標準IO庫IO標準庫的條件狀態(tài)流必須處于無錯誤狀態(tài),才能用于輸入或輸出。檢測流是否用的最簡單的方法是檢查其真值:if(cin)//oktousecin,itisinavalidstatewhile(cin>>word)//ok:readoperationsuccessful...輸出緩沖區(qū)的管理每個IO對象管理一個緩沖區(qū),用于存儲程序讀寫的數據。如有下面語句:os<<"pleaseenteravalue:";程序正常結束。作為main返回工作的一部分,將清空所有輸出緩沖區(qū)。在一些不確定的時候,緩沖區(qū)可能已經滿了,在這種情況下,緩沖區(qū)將會在寫下一個值之前刷新。用操縱符(第1.2.2節(jié))顯式地刷新緩沖區(qū),例如行結束符endl。在每次輸出操作執(zhí)行完后,用unitbuf操作符設置流的內部狀態(tài),從而清空緩沖區(qū)??蓪⑤敵隽髋c輸入流關聯(tie)起來。在這種情況下,在讀輸入流時將刷新其關聯的輸出緩沖區(qū)。輸出緩沖區(qū)的刷新們的程序已經使用過endl操縱符,用于輸出一個換行符并刷新緩沖區(qū)。除此之外,C++語言還提供了另外兩個類似的操縱符。第一個經常使用的flush,用于刷新流,但不在輸出中添加任何字符。第二個則是比較少用的ends,這個操縱符在緩沖區(qū)中插入空字符null,然后后刷新它:cout<<"hi!"<<flush;//flushesthebuffer;addsnodatacout<<"hi!"<<ends;//insertsanull,thenflushesthebuffercout<<"hi!"<<endl;//insertsanewline,thenflushesthebufferunitbuf操縱符如果需要刷新所有輸出,最好使用unitbuf操縱符。這個操縱符在每次執(zhí)行完寫操作后都刷新流:cout<<unitbuf<<"first"<<"second"<<nounitbuf;等價于:cout<<"first"<<flush<<"second"<<flush;nounitbuf操縱符將流恢復為使用正常的、由系統(tǒng)管理的緩沖區(qū)刷新方式。文件的輸入和輸出文件流對象的使用//constructanifstreamandbindittothefilenamedifileifstreaminfile(ifile.c_str());//ofstreamoutputfileobjecttowritefilenamedofileofstreamoutfile(ofile.c_str());上述代碼定義并打開了一對fstream對象。infile是讀的流,而outfile則是寫的流。為ifstream或者ofstream對象提供文件名作為初始化式,就相當于打開了特定的文件。上述語句將infile定義為讀文件的流對象,將outfile定義為寫文件的對象。這兩個對象都沒有捆綁具體的文件。在使用fstream對象之前,還必須使這些對象捆綁要讀寫的文件:infile.open("in");//openfilenamed"in"inthecurrentdirectoryoutfile.open("out");//openfilenamed"out"inthecurrentdirectory由于歷史原因,IO標準庫使用C風格字符串(第4.3節(jié))而不是C++strings類型的字符串作為文件名。在創(chuàng)建fstream對象時,如果調用open或使用文件名作初始化式,需要傳遞的實參應為C風格字符串,而不是標準庫strings對象。程序常常從標準輸入獲得文件名。通常,比較好的方法是將文件名讀入string對象,而不是C風格字符數組。假設要使用的文件名保存在string對象中,則可調用c_str成員(第4.3.2節(jié))獲取C風格字符串。檢查文件打開是否成功打開文件后,通常要檢驗打開是否成功,這是一個好習慣://

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論