版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
C++模板1.1模板概論c++提供了函數(shù)模板(functiontemplate.)所謂函數(shù)模板,實際上是建立一個通用函數(shù),其函數(shù)類型和形參類型不具體制定,用一個虛擬的類型來代表。這個通用函數(shù)就成為函數(shù)模板。凡是函數(shù)體相同的函數(shù)都可以用這個模板代替,不必定義多個函數(shù),只需在模板中定義一次即可。在調(diào)用函數(shù)時系統(tǒng)會根據(jù)實參的類型來取代模板中的虛擬類型,從而實現(xiàn)不同函數(shù)的功能。c++提供兩種模板機(jī)制:函數(shù)模板和類模板類屬-類型參數(shù)化,又稱參數(shù)模板總結(jié):模板把函數(shù)或類要處理的數(shù)據(jù)類型參數(shù)化,表現(xiàn)為參數(shù)的多態(tài)性,成為類屬。模板用于表達(dá)邏輯結(jié)構(gòu)相同,但具體數(shù)據(jù)元素類型不同的數(shù)據(jù)對象的通用行為。1.2函數(shù)模板1.2.1什么是函數(shù)模板?//交換int數(shù)據(jù)voidSwapInt(int&a,int&b){ inttemp=a; a=b; b=temp;}//交換char數(shù)據(jù)voidSwapChar(char&a,char&b){ chartemp=a; a=b; b=temp;}//問題:如果我要交換double類型數(shù)據(jù),那么還需要些一個double類型數(shù)據(jù)交換的函數(shù)//繁瑣,寫的函數(shù)越多,當(dāng)交換邏輯發(fā)生變化的時候,所有的函數(shù)都需要修改,無形當(dāng)中增加了代碼的維護(hù)難度//如果能把類型作為參數(shù)傳遞進(jìn)來就好了,傳遞int就是Int類型交換,傳遞char就是char類型交換//我們有一種技術(shù),可以實現(xiàn)類型的參數(shù)化---函數(shù)模板//class和typename都是一樣的,用哪個都可以template<classT>voidMySwap(T&a,T&b){ Ttemp=a; a=b; b=temp;}voidtest01(){ inta=10; intb=20; cout<<"a:"<<a<<"b:"<<b<<endl; //1.這里有個需要注意點,函數(shù)模板可以自動推導(dǎo)參數(shù)的類型 MySwap(a,b); cout<<"a:"<<a<<"b:"<<b<<endl; charc1='a'; charc2='b'; cout<<"c1:"<<c1<<"c2:"<<c2<<endl; //2.函數(shù)模板可以自動類型推導(dǎo),那么也可以顯式指定類型 MySwap<char>(c1,c2); cout<<"c1:"<<c1<<"c2:"<<c2<<endl;}用模板是為了實現(xiàn)泛型,可以減輕編程的工作量,增強(qiáng)函數(shù)的重用性。1.2.2課堂練習(xí)使用函數(shù)模板實現(xiàn)對char和int類型數(shù)組進(jìn)行排序?//模板打印函數(shù)template<classT>voidPrintArray(Tarr[],intlen){ for(inti=0;i<len;i++){ cout<<arr[i]<<""; } cout<<endl;}//模板排序函數(shù)template<classT>voidMySort(Tarr[],intlen){ for(inti=0;i<len;i++){ for(intj=len-1;j>i;j--){ if(arr[j]>arr[j-1]){ Ttemp=arr[j-1]; arr[j-1]=arr[j]; arr[j]=temp; } } }}voidtest(){ //char數(shù)組 chartempChar[]="aojtifysn"; intcharLen=strlen(tempChar); //int數(shù)組 inttempInt[]={7,4,2,9,8,1}; intintLen=sizeof(tempInt)/sizeof(int); //排序前打印函數(shù) PrintArray(tempChar,charLen); PrintArray(tempInt,intLen); //排序 MySort(tempChar,charLen); MySort(tempInt,intLen); //排序后打印 PrintArray(tempChar,charLen); PrintArray(tempInt,intLen);}1.3函數(shù)模板和普通函數(shù)區(qū)別函數(shù)模板不允許自動類型轉(zhuǎn)化普通函數(shù)能夠自動進(jìn)行類型轉(zhuǎn)化//函數(shù)模板template<classT>TMyPlus(Ta,Tb){ Tret=a+b; returnret;}//普通函數(shù)intMyPlus(inta,charb){ intret=a+b; returnret;}voidtest02(){ inta=10; charb='a'; //調(diào)用函數(shù)模板,嚴(yán)格匹配類型 MyPlus(a,a); MyPlus(b,b); //調(diào)用普通函數(shù) MyPlus(a,b); //調(diào)用普通函數(shù)普通函數(shù)可以隱式類型轉(zhuǎn)換 MyPlus(b,a); //結(jié)論: //函數(shù)模板不允許自動類型轉(zhuǎn)換,必須嚴(yán)格匹配類型 //普通函數(shù)可以進(jìn)行自動類型轉(zhuǎn)換}1.4函數(shù)模板和普通函數(shù)在一起調(diào)用規(guī)則c++編譯器優(yōu)先考慮普通函數(shù)可以通過空模板實參列表的語法限定編譯器只能通過模板匹配函數(shù)模板可以像普通函數(shù)那樣可以被重載如果函數(shù)模板可以產(chǎn)生一個更好的匹配,那么選擇模板//函數(shù)模板template<classT>TMyPlus(Ta,Tb){ Tret=a+b; returnret;}//普通函數(shù)intMyPlus(inta,intb){ intret=a+b; returnret;}voidtest03(){ inta=10; intb=20; charc='a'; chard='b'; //如果函數(shù)模板和普通函數(shù)都能匹配,c++編譯器優(yōu)先考慮普通函數(shù) cout<<MyPlus(a,b)<<endl; //如果我必須要調(diào)用函數(shù)模板,那么怎么辦? cout<<MyPlus<>(a,b)<<endl; //此時普通函數(shù)也可以匹配,因為普通函數(shù)可以自動類型轉(zhuǎn)換 //但是此時函數(shù)模板能夠有更好的匹配 //如果函數(shù)模板可以產(chǎn)生一個更好的匹配,那么選擇模板 cout<<MyPlus(c,d);}//函數(shù)模板重載template<classT>TMyPlus(Ta,Tb,Tc){ Tret=a+b+c; returnret;}voidtest04(){ inta=10; intb=20; intc=30; cout<<MyPlus(a,b,c)<<endl; //如果函數(shù)模板和普通函數(shù)都能匹配,c++編譯器優(yōu)先考慮普通函數(shù)}1.5模板機(jī)制剖析思考:為什么函數(shù)模板可以和普通函數(shù)放在一起?c++編譯器是如何實現(xiàn)函數(shù)模板機(jī)制的?1.5.1編譯過程hello.cpp程序是高級c語言程序,這種程序易于被人讀懂。為了在系統(tǒng)上運(yùn)行hello.c程序,每一條c語句都必須轉(zhuǎn)化為低級的機(jī)器指令。然后將這些機(jī)器指令打包成可執(zhí)行目標(biāo)文件格式,并以二進(jìn)制形式存儲于磁盤中。預(yù)處理(Pre-processing)->編譯(Compiling)->匯編(Assembling)->鏈接(Linking)1.5.2模板實現(xiàn)機(jī)制函數(shù)模板機(jī)制結(jié)論:編譯器并不是把函數(shù)模板處理成能夠處理任何類型的函數(shù)函數(shù)模板通過具體類型產(chǎn)生不同的函數(shù)編譯器會對函數(shù)模板進(jìn)行兩次編譯,在聲明的地方對模板代碼本身進(jìn)行編譯,在調(diào)用的地方對參數(shù)替換后的代碼進(jìn)行編譯。1.6模板的局限性假設(shè)有如下模板函數(shù): template<classT> voidf(Ta,Tb) {…}如果代碼實現(xiàn)時定義了賦值操作a=b,但是T為數(shù)組,這種假設(shè)就不成立了同樣,如果里面的語句為判斷語句if(a>b),但T如果是結(jié)構(gòu)體,該假設(shè)也不成立,另外如果是傳入的數(shù)組,數(shù)組名為地址,因此它比較的是地址,而這也不是我們所希望的操作。 總之,編寫的模板函數(shù)很可能無法處理某些類型,另一方面,有時候通用化是有意義的,但C++語法不允許這樣做。為了解決這種問題,可以提供模板的重載,為這些特定的類型提供具體化的模板。classPerson{public: Person(stringname,intage) { this->mName=name; this->mAge=age; } stringmName; intmAge;};//普通交換函數(shù)template<classT>voidmySwap(T&a,T&b){ Ttemp=a; a=b; b=temp;}//第三代具體化,顯示具體化的原型和定意思以template<>開頭,并通過名稱來指出類型//具體化優(yōu)先于常規(guī)模板template<>voidmySwap<Person>(Person&p1,Person&p2){ stringnameTemp; intageTemp; nameTemp=p1.mName; p1.mName=p2.mName; p2.mName=nameTemp; ageTemp=p1.mAge; p1.mAge=p2.mAge; p2.mAge=ageTemp;}voidtest(){ PersonP1("Tom",10); PersonP2("Jerry",20); cout<<"P1Name="<<P1.mName<<"P1Age="<<P1.mAge<<endl; cout<<"P2Name="<<P2.mName<<"P2Age="<<P2.mAge<<endl; mySwap(P1,P2); cout<<"P1Name="<<P1.mName<<"P1Age="<<P1.mAge<<endl; cout<<"P2Name="<<P2.mName<<"P2Age="<<P2.mAge<<endl;}1.7類模板1.7.1類模板基本概念類模板和函數(shù)模板的定義和使用類似,我們已經(jīng)進(jìn)行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數(shù)據(jù)類型不同。類模板用于實現(xiàn)類所需數(shù)據(jù)的類型參數(shù)化template<classNameType,classAgeType>classPerson{public: Person(NameTypename,AgeTypeage) { this->mName=name; this->mAge=age; } voidshowPerson() { cout<<"name:"<<this->mName<<"age:"<<this->mAge<<endl; }public: NameTypemName; AgeTypemAge;};voidtest01(){ //PersonP1("德瑪西亞",18);//類模板不能進(jìn)行類型自動推導(dǎo) Person<string,int>P1("德瑪西亞",18); P1.showPerson();}1.7.2類模板做函數(shù)參數(shù)//類模板template<classNameType,classAgeType>classPerson{public: Person(NameTypename,AgeTypeage){ this->mName=name; this->mAge=age; } voidPrintPerson(){ cout<<"Name:"<<this->mName<<"Age:"<<this->mAge<<endl; }public: NameTypemName; AgeTypemAge;};//類模板做函數(shù)參數(shù)voidDoBussiness(Person<string,int>&p){ p.mAge+=20; p.mName+="_vip"; p.PrintPerson();}intmain(){ Person<string,int>p("John",30); DoBussiness(p); system("pause"); returnEXIT_SUCCESS;}1.7.3類模板派生普通類//類模板template<classT>classMyClass{public: MyClass(Tproperty){ this->mProperty=property; }public: TmProperty;};//子類實例化的時候需要具體化的父類,子類需要知道父類的具體類型是什么樣的//這樣c++編譯器才能知道給子類分配多少內(nèi)存//普通派生類classSubClass:publicMyClass<int>{public: SubClass(intb):MyClass<int>(20){ this->mB=b; }public: intmB;};1.7.4類模板派生類模板//父類類模板template<classT>classBase{ Tm;};template<classT>classChild2:publicBase<double>//繼承類模板的時候,必須要確定基類的大小{public: TmParam;};voidtest02(){ Child2<int>d2;}1.7.5類模板類內(nèi)實現(xiàn)template<classNameType,classAgeType>classPerson{public: Person(NameTypename,AgeTypeage) { this->mName=name; this->mAge=age; } voidshowPerson() { cout<<"name:"<<this->mName<<"age:"<<this->mAge<<endl; }public: NameTypemName; AgeTypemAge;};voidtest01(){ //PersonP1("德瑪西亞",18);//類模板不能進(jìn)行類型自動推導(dǎo) Person<string,int>P1("德瑪西亞",18); P1.showPerson();}1.7.6類模板類外實現(xiàn)#define_CRT_SECURE_NO_WARNINGS#include<iostream>#include<string>usingnamespacestd;template<classT1,classT2>classPerson{public: Person(T1name,T2age); voidshowPerson();public: T1mName; T2mAge;};//類外實現(xiàn)template<classT1,classT2>Person<T1,T2>::Person(T1name,T2age){ this->mName=name; this->mAge=age;}template<classT1,classT2>voidPerson<T1,T2>::showPerson(){ cout<<"Name:"<<this->mName<<"Age:"<<this->mAge<<endl;}voidtest(){ Person<string,int>p("Obama",20); p.showPerson();}intmain(){ test(); system("pause"); returnEXIT_SUCCESS;}1.7.7類模板頭文件和源文件分離問題Person.hpp#pragmaoncetemplate<classT1,classT2>classPerson{public: Person(T1name,T2age); voidShowPerson();public: T1mName; T2mAge;};template<classT1,classT2>Person<T1,T2>::Person(T1name,T2age){ this->mName=name; this->mAge=age;}template<classT1,classT2>voidPerson<T1,T2>::ShowPerson(){ cout<<"Name:"<<this->mName<<"Age:"<<this->mAge<<endl;}main.cpp#define_CRT_SECURE_NO_WARNINGS#include<iostream>usingnamespacestd;#include<string>#include"Person.hpp"http://模板二次編譯//編譯器編譯源碼逐個編譯單元編譯的intmain(){ Person<string,int>p("Obama",20); p.ShowPerson(); system("pause"); returnEXIT_SUCCESS;}結(jié)論:案例代碼在qt編譯器順利通過編譯并執(zhí)行,但是在Linux和vs編輯器下如果只包含頭文件,那么會報錯鏈接錯誤,需要包含cpp文件,但是如果類模板中有友元類,那么編譯失敗!解決方案:類模板的聲明和實現(xiàn)放到一個文件中,我們把這個文件命名為.hpp(這個是個約定的規(guī)則,并不是標(biāo)準(zhǔn),必須這么寫).原因:類模板需要二次編譯,在出現(xiàn)模板的地方編譯一次,在調(diào)用模板的地方再次編譯。C++編譯規(guī)則為獨(dú)立編譯。模板類碰到友元函數(shù)#define_CRT_SECURE_NO_WARNINGS#include<iostream>usingnamespacestd;#include<string>template<classT1,classT2>classPerson;//告訴編譯器這個函數(shù)模板是存在template<classT1,classT2>voidPrintPerson2(Person<T1,T2>&p);//友元函數(shù)在類內(nèi)實現(xiàn)template<classT1,classT2>classPerson{ //1.友元函數(shù)在類內(nèi)實現(xiàn) friendvoidPrintPerson(Person<T1,T2>&p){ cout<<"Name:"<<p.mName<<"Age:"<<p.mAge<<endl; } //2.友元函數(shù)類外實現(xiàn) //告訴編譯器這個函數(shù)模板是存在 friendvoidPrintPerson2<>(Person<T1,T2>&p); //3.類模板碰到友元函數(shù)模板 template<classU1,classU2> friendvoidPrintPerson(Person<U1,U2>&p);public: Person(T1name,T2age){ this->mName=name; this->mAge=age; } voidshowPerson(){ cout<<"Name:"<<this->mName<<"Age:"<<this->mAge<<endl; }private: T1mName; T2mAge;};voidtest01(){ Person<string,int>p("Jerry",20); PrintPerson(p);}//類模板碰到友元函數(shù)//友元函數(shù)類外實現(xiàn)加上<>空參數(shù)列表,告訴編譯去匹配函數(shù)模板template<classT1,classT2>voidPrintPerson2(Person<T1,T2>&p){ cout<<"Name2:"<<p.mName<<"Age2:"<<p.mAge<<endl;}voidtest02(){ Person<string,int>p("Jerry",20); PrintPerson2(p);//不寫可以編譯通過,寫了之后,會找PrintPerson2的普通函數(shù)調(diào)用,因為寫了普通函數(shù)PrintPerson2的聲明 }intmain(){ //test01(); test02(); system("pause"); returnEXIT_SUCCESS;}類模板的應(yīng)用設(shè)計一個數(shù)組模板類(MyArray),完成對不同類型元素的管理#pragmaoncetemplate<classT>classMyArray{public: explicitMyArray(intcapacity) { this->m_Capacity=capacity; this->m_Size=0; //如果T是對象,那么這個對象必須提供默認(rèn)的構(gòu)造函數(shù) pAddress=newT[this->m_Capacity]; } //拷貝構(gòu)造 MyArray(constMyArray&arr) { this->m_Capacity=arr.m_Capacity; this->m_Size=arr.m_Size; this->pAddress=newT[this->m_Capacity]; for(inti=0;i<this->m_Size;i++) { this->pAddress[i]=arr.pAddress[i]; } } //重載[]操作符arr[0] T&operator[](intindex) { returnthis->pAddress[index]; } //尾插法 voidPush_back(constT&val) { if(this->m_Capacity==this->m_Size) { return; } this->pAddress[this->m_Size]=val; this->m_Size++; } voidPop_back() { if(this->m_Size==0) { return; } this->m_Size--; } int getSize() { returnthis->m_Size; } //析構(gòu) ~MyArray() { if(this->pAddress!=NULL) { delete[]this->pAddress; this->pAddress=NULL; this->m_Capacity=0; this->m_Size=0; } }private: T*pAddress;//指向一個堆空間,這個空間存儲真正的數(shù)據(jù) intm_Capacity;//容量 intm_Size;//大小};測試代碼:classPerson{public: Person(){} Person(stringname,intage){ this->mName=name; this->mAge=age; }public: stringmName; intmAge;};voidPrintMyArrayInt(MyArray<int>&arr){ for(inti=0;i<arr.getSize();i++){ cout<<arr[i]<<""; } cout<<endl;}voidPrintMyPerson(MyArray<Person>&personArr){ for(inti=0;i<personArr.getSize();i++){ cout<<"姓名:"<<personArr[i].mName<<"年齡:"<<personArr[i].mAge<<endl; } } MyArray<int>myArrayInt(10); for(inti=0;i<9;i++) { myArrayInt.Push_back(i); } myArrayInt.Push_back(100); PrintMyArrayInt(myArrayInt);MyArray<Person>myArrayPerson(10); Personp1("德瑪西亞",30); Personp2("提莫",20); Personp3("孫悟空",18); Personp4("趙信",15); Personp5("趙云",24); myArrayPerson.Push_back(p1); myArrayPerson.Push_back(p2); myArrayPerson.Push_back(p3); myArrayPerson.Push_back(p4); myArrayPerson.Push_back(p5);C++類型轉(zhuǎn)換類型轉(zhuǎn)換(cast)是將一種數(shù)據(jù)類型轉(zhuǎn)換成另一種數(shù)據(jù)類型。例如,如果將一個整型值賦給一個浮點類型的變量,編譯器會暗地里將其轉(zhuǎn)換成浮點類型。轉(zhuǎn)換是非常有用的,但是它也會帶來一些問題,比如在轉(zhuǎn)換指針時,我們很可能將其轉(zhuǎn)換成一個比它更大的類型,但這可能會破壞其他的數(shù)據(jù)。應(yīng)該小心類型轉(zhuǎn)換,因為轉(zhuǎn)換也就相當(dāng)于對編譯器說:忘記類型檢查,把它看做其他的類型。一般情況下,盡量少的去使用類型轉(zhuǎn)換,除非用來解決非常特殊的問題。無論什么原因,任何一個程序如果使用很多類型轉(zhuǎn)換都值得懷疑.標(biāo)準(zhǔn)c++提供了一個顯示的轉(zhuǎn)換的語法,來替代舊的C風(fēng)格的類型轉(zhuǎn)換。使用C風(fēng)格的強(qiáng)制轉(zhuǎn)換可以把想要的任何東西轉(zhuǎn)換成我們需要的類型。那為什么還需要一個新的C++類型的強(qiáng)制轉(zhuǎn)換呢?新類型的強(qiáng)制轉(zhuǎn)換可以提供更好的控制強(qiáng)制轉(zhuǎn)換過程,允許控制各種不同種類的強(qiáng)制轉(zhuǎn)換。C++風(fēng)格的強(qiáng)制轉(zhuǎn)換其他的好處是,它們能更清晰的表明它們要干什么。程序員只要掃一眼這樣的代碼,就能立即知道一個強(qiáng)制轉(zhuǎn)換的目的。2.1靜態(tài)轉(zhuǎn)換(static_cast)用于類層次結(jié)構(gòu)中基類(父類)和派生類(子類)之間指針或引用的轉(zhuǎn)換。進(jìn)行上行轉(zhuǎn)換(把派生類的指針或引用轉(zhuǎn)換成基類表示)是安全的;進(jìn)行下行轉(zhuǎn)換(把基類指針或引用轉(zhuǎn)換成派生類表示)時,由于沒有動態(tài)類型檢查,所以是不安全的。用于基本數(shù)據(jù)類型之間的轉(zhuǎn)換,如把int轉(zhuǎn)換成char,把char轉(zhuǎn)換成int。這種轉(zhuǎn)換的安全性也要開發(fā)人員來保證。classAnimal{};classDog:publicAnimal{};classOther{};//基礎(chǔ)數(shù)據(jù)類型轉(zhuǎn)換voidtest01(){ chara='a'; doubleb=static_cast<double>(a);}//繼承關(guān)系指針互相轉(zhuǎn)換voidtest02(){ //繼承關(guān)系指針轉(zhuǎn)換 Animal*animal01=NULL; Dog*dog01=NULL; //子類指針轉(zhuǎn)成父類指針,安全 Animal*animal02=static_cast<Animal*>(dog01); //父類指針轉(zhuǎn)成子類指針,不安全 Dog*dog02=static_cast<Dog*>(animal01);}//繼承關(guān)系引用相互轉(zhuǎn)換voidtest03(){ Animalani_ref; Dogdog_ref; //繼承關(guān)系指針轉(zhuǎn)換 Animal&animal01=ani_ref; Dog&dog01=dog_ref; //子類指針轉(zhuǎn)成父類指針,安全 Animal&animal02=static_cast<Animal&>(dog01); //父類指針轉(zhuǎn)成子類指針,不安全 Dog&dog02=static_cast<Dog&>(animal01);}//無繼承關(guān)系指針轉(zhuǎn)換voidtest04(){ Animal*animal01=NULL; Other*other01=NULL; //轉(zhuǎn)換失敗 //Animal*animal02=static_cast<Animal*>(other01);}2.2動態(tài)轉(zhuǎn)換(dynamic_cast)ynamic_cast主要用于類層次間的上行轉(zhuǎn)換和下行轉(zhuǎn)換;在類層次間進(jìn)行上行轉(zhuǎn)換時,dynamic_cast和static_cast的效果是一樣的;在進(jìn)行下行轉(zhuǎn)換時,dynamic_cast具有類型檢查的功能,比static_cast更安全;classAnimal{public: virtualvoidShowName()=0;};classDog:publicAnimal{ virtualvoidShowName(){ cout<<"Iamadog!"<<endl; }};classOther{public: voidPrintSomething(){ cout<<"我是其他類!"<<endl; }};//普通類型轉(zhuǎn)換voidtest01(){ //不支持基礎(chǔ)數(shù)據(jù)類型 inta=10; //doublea=dynamic_cast<double>(a);}//繼承關(guān)系指針voidtest02(){ Animal*animal01=NULL; Dog*dog01=newDog; //子類指針轉(zhuǎn)換成父類指針可以 Animal*animal02=dynamic_cast<Animal*>(dog01); animal02->ShowName(); //父類指針轉(zhuǎn)換成子類指針不可以 //Dog*dog02=dynamic_cast<Dog*>(animal01);}//繼承關(guān)系引用voidtest03(){ Dogdog_ref; Dog&dog01=dog_ref; //子類引用轉(zhuǎn)換成父類引用可以 Animal&animal02=dynamic_cast<Animal&>(dog01); animal02.ShowName();}//無繼承關(guān)系指針轉(zhuǎn)換voidtest04(){ Animal*animal01=NULL; Other*other=NULL; //不可以 //Animal*animal02=dynamic_cast<Animal*>(other);}2.3常量轉(zhuǎn)換(const_cast)該運(yùn)算符用來修改類型的const屬性。。常量指針被轉(zhuǎn)化成非常量指針,并且仍然指向原來的對象;常量引用被轉(zhuǎn)換成非常量引用,并且仍然指向原來的對象;注意:不能直接對非指針和非引用的變量使用const_cast操作符去直接移除它的const.//常量指針轉(zhuǎn)換成非常量指針voidtest01(){ constint*p=NULL; int*np=const_cast<int*>(p); int*pp=NULL; constint*npp=const_cast<constint*>(pp); constinta=10;//不能對非指針或非引用進(jìn)行轉(zhuǎn)換 //intb=const_cast<int>(a);}//常量引用轉(zhuǎn)換成非常量引用voidtest02(){intnum=10; int&refNum=num; constint&refNum2=const_cast<constint&>(refNum); }2.3重新解釋轉(zhuǎn)換(reinterpret_cast)。。。。。這是最不安全的一種轉(zhuǎn)換機(jī)制,最有可能出問題。主要用于將一種數(shù)據(jù)類型從一種類型轉(zhuǎn)換為另一種類型。它可以將一個指針轉(zhuǎn)換成一個整數(shù),也可以將一個整數(shù)轉(zhuǎn)換成一個指針.3.C++異常3.1異?;靖拍頑jarneStroustrup說:提供異常的基本目的就是為了處理上面的問題。基本思想是:讓一個函數(shù)在發(fā)現(xiàn)了自己無法處理的錯誤時拋出(throw)一個異常,然后它的(直接或者間接)調(diào)用者能夠處理這個問題。也就是《C++primer》中說的:將問題檢測和問題處理相分離。一種思想:在所有支持異常處理的編程語言中(例如java),要認(rèn)識到的一個思想:在異常處理過程中,由問題檢測代碼可以拋出一個對象給問題處理代碼,通過這個對象的類型和內(nèi)容,實際上完成了兩個部分的通信,通信的內(nèi)容是“出現(xiàn)了什么錯誤”。當(dāng)然,各種語言對異常的具體實現(xiàn)有著或多或少的區(qū)別,但是這個通信的思想是不變的。一句話:異常處理就是處理程序中的錯誤。所謂錯誤是指在程序運(yùn)行的過程中發(fā)生的一些異常事件(如:除0溢出,數(shù)組下標(biāo)越界,所要讀取的文件不存在,空指針,內(nèi)存不足等等)?;仡櫼幌拢何覀円郧熬帉懗绦蚴侨绾翁幚懋惓#吭贑語言的世界中,對錯誤的處理總是圍繞著兩種方法:一是使用整型的返回值標(biāo)識錯誤;二是使用errno宏(可以簡單的理解為一個全局整型變量)去記錄錯誤。當(dāng)然C++中仍然是可以用這兩種方法的。這兩種方法最大的缺陷就是會出現(xiàn)不一致問題。例如有些函數(shù)返回1表示成功,返回0表示出錯;而有些函數(shù)返回0表示成功,返回非0表示出錯。還有一個缺點就是函數(shù)的返回值只有一個,你通過函數(shù)的返回值表示錯誤代碼,那么函數(shù)就不能返回其他的值。當(dāng)然,你也可以通過指針或者C++的引用來返回另外的值,但是這樣可能會令你的程序略微晦澀難懂。c++異常機(jī)制相比C語言異常處理的優(yōu)勢?函數(shù)的返回值可以忽略,但異常不可忽略。如果程序出現(xiàn)異常,但是沒有被捕獲,程序就會終止,這多少會促使程序員開發(fā)出來的程序更健壯一點。而如果使用C語言的error宏或者函數(shù)返回值,調(diào)用者都有可能忘記檢查,從而沒有對錯誤進(jìn)行處理,結(jié)果造成程序莫名其面的終止或出現(xiàn)錯誤的結(jié)果。整型返回值沒有任何語義信息。而異常卻包含語義信息,有時你從類名就能夠體現(xiàn)出來。整型返回值缺乏相關(guān)的上下文信息。異常作為一個類,可以擁有自己的成員,這些成員就可以傳遞足夠的信息。異常處理可以在調(diào)用跳級。這是一個代碼編寫時的問題:假設(shè)在有多個函數(shù)的調(diào)用棧中出現(xiàn)了某個錯誤,使用整型返回碼要求你在每一級函數(shù)中都要進(jìn)行處理。而使用異常處理的棧展開機(jī)制,只需要在一處進(jìn)行處理就可以了,不需要每級函數(shù)都處理。//如果判斷返回值,那么返回值是錯誤碼還是結(jié)果?//如果不判斷返回值,那么b==0時候,程序結(jié)果已經(jīng)不正確//A寫的代碼intA_MyDivide(inta,intb){ if(b==0){ return-1; } returna/b;}//B寫的代碼intB_MyDivide(inta,intb){ intba=a+100; intbb=b; intret=A_MyDivide(ba,bb);//由于B沒有處理異常,導(dǎo)致B結(jié)果運(yùn)算錯誤 returnret;}//C寫的代碼intC_MyDivide(){ inta=10; intb=0; intret=B_MyDivide(a,b);//更嚴(yán)重的是,由于B沒有繼續(xù)拋出異常,導(dǎo)致C的代碼沒有辦法捕獲異常 if(ret==-1){ return-1; } else{ returnret; }}//所以,我們希望://1.異常應(yīng)該捕獲,如果你捕獲,可以,那么異常必須繼續(xù)拋給上層函數(shù),你不處理,不代表你的上層不處理//2.這個例子,異常沒有捕獲的結(jié)果就是運(yùn)行結(jié)果錯的一塌糊涂,結(jié)果未知,未知的結(jié)果程序沒有必要執(zhí)行下去3.2異常語法3.2.1異常基本語法intA_MyDivide(inta,intb){ if(b==0){ throw0; } returna/b;}//B寫的代碼B寫代碼比較粗心,忘記處理異常intB_MyDivide(inta,intb){ intba=a; intbb=b; intret=A_MyDivide(ba,bb)+100;//由于B沒有處理異常,導(dǎo)致B結(jié)果運(yùn)算錯誤 returnret;}//C寫的代碼intC_MyDivide(){ inta=10; intb=0; intret=0;//沒有處理異常,程序直接中斷執(zhí)行#if1 ret=B_MyDivide(a,b);//處理異常#else try{ ret=B_MyDivide(a,b);//更嚴(yán)重的是,由于B沒有繼續(xù)拋出異常,導(dǎo)致C的代碼沒有辦法捕獲異常 } catch(inte){ cout<<"C_MyDivideCallB_MyDivide除數(shù)為:"<<e<<endl; }#endif returnret;}intmain(){ C_MyDivide(); system("pause"); returnEXIT_SUCCESS;}總結(jié):若有異常則通過throw操作創(chuàng)建一個異常對象并拋出。將可能拋出異常的程序段放到try塊之中。如果在try段執(zhí)行期間沒有引起異常,那么跟在try后面的catch字句就不會執(zhí)行。catch子句會根據(jù)出現(xiàn)的先后順序被檢查,匹配的catch語句捕獲并處理異常(或繼續(xù)拋出異常)如果匹配的處理未找到,則運(yùn)行函數(shù)terminate將自動被調(diào)用,其缺省功能調(diào)用abort終止程序。處理不了的異常,可以在catch的最后一個分支,使用throw,向上拋。c++異常處理使得異常的引發(fā)和異常的處理不必在一個函數(shù)中,這樣底層的函數(shù)可以著重解決具體問題,而不必過多的考慮異常的處理。上層調(diào)用者可以在適當(dāng)?shù)奈恢迷O(shè)計對不同類型異常的處理。3.2.2異常嚴(yán)格類型匹配異常機(jī)制和函數(shù)機(jī)制互不干涉,但是捕捉方式是通過嚴(yán)格類型匹配。voidTestFunction(){ cout<<"開始拋出異常..."<<endl; //throw10;//拋出int類型異常 //throw'a';//拋出char類型異常 //throw"abcd";//拋出char*類型異常 stringex="stringexception!"; throwex;}intmain(){ try{ TestFunction(); } catch(int){ cout<<"拋出Int類型異常!"<<endl; } catch(char){ cout<<"拋出Char類型異常!"<<endl; } catch(char*){ cout<<"拋出Char*類型異常!"<<endl; } catch(string){ cout<<"拋出string類型異常!"<<endl; } //捕獲所有異常 catch(...){ cout<<"拋出其他類型異常!"<<endl; } system("pause"); returnEXIT_SUCCESS;}3.2.3棧解旋(unwinding)異常被拋出后,從進(jìn)入try塊起,到異常被拋擲前,這期間在棧上構(gòu)造的所有對象,都會被自動析構(gòu)。析構(gòu)的順序與構(gòu)造的順序相反,這一過程稱為棧的解旋(unwinding).classPerson{public: Person(stringname){ mName=name; cout<<mName<<"對象被創(chuàng)建!"<<endl; } ~Person(){ cout<<mName<<"對象被析構(gòu)!"<<endl; }public: stringmName;};voidTestFunction(){ Personp1("aaa"); Personp2("bbb"); Personp3("ccc"); //拋出異常 throw10;}intmain(){ try{ TestFunction(); } catch(...){ cout<<"異常被捕獲!"<<endl; } system("pause"); returnEXIT_SUCCESS;}3.2.4異常接口聲明為了加強(qiáng)程序的可讀性,可以在函數(shù)聲明中列出可能拋出異常的所有類型,例如:voidfunc()throw(A,B,C);這個函數(shù)func能夠且只能拋出類型A,B,C及其子類型的異常。如果在函數(shù)聲明中沒有包含異常接口聲明,則此函數(shù)可以拋任何類型的異常,例如:voidfunc()一個不拋任何類型異常的函數(shù)可聲明為:voidfunc()throw()如果一個函數(shù)拋出了它的異常接口聲明所不允許拋出的異常,unexcepted函數(shù)會被調(diào)用,該函數(shù)默認(rèn)行為調(diào)用terminate函數(shù)中斷程序。//可拋出所有類型異常voidTestFunction01(){ throw10;}//只能拋出intcharchar*類型異常voidTestFunction02()throw(int,char,char*){ stringexception="error!"; throwexception;}//不能拋出任何類型異常voidTestFunction03()throw(){ throw10;}intmain(){ try{ //TestFunction01(); //TestFunction02(); //TestFunction03(); } catch(...){ cout<<"捕獲異常!"<<endl; } system("pause"); returnEXIT_SUCCESS;}請分別在qtvslinux下做測試!QtandLinux正確!3.2.5異常變量生命周期throw的異常是有類型的,可以是數(shù)字、字符串、類對象。throw的異常是有類型的,catch需嚴(yán)格匹配異常類型。classMyException{public: MyException(){ cout<<"異常變量構(gòu)造"<<endl; }; MyException(constMyException&e) { cout<<"拷貝構(gòu)造"<<endl; } ~MyException() { cout<<"異常變量析構(gòu)"<<endl; }};voidDoWork(){ thrownewMyException();//test12都用throwMyExecption();}voidtest01(){ try { DoWork(); } catch(MyExceptione) { cout<<"捕獲異常"<<endl; }}voidtest02(){ try { DoWork(); } catch(MyException&e) { cout<<"捕獲異常"<<endl; }}voidtest03(){ try { DoWork(); } catch(MyException*e) { cout<<"捕獲異常"<<endl; deletee; }}3.2.6異常的多態(tài)使用//異?;恈lassBaseException{public: virtualvoidprintError(){};};//空指針異常classNullPointerException:publicBaseException{public: virtualvoidprintError(){ cout<<"空指針異常!"<<endl; }};//越界異常classOutOfRangeException:publicBaseException{public: virtualvoidprintError(){ cout<<"越界異常!"<<endl; }};voiddoWork(){ throwNullPointerException();}voidtest(){ try{ doWork(); } catch(BaseException&ex){ ex.printError(); }}3.3C++標(biāo)準(zhǔn)異常庫3.3.1標(biāo)準(zhǔn)庫介紹標(biāo)準(zhǔn)庫中也提供了很多的異常類,它們是通過類繼承組織起來的。異常類繼承層級結(jié)構(gòu)圖如下:每個類所在的頭文件在圖下方標(biāo)識出來。標(biāo)準(zhǔn)異常類的成員:①在上述繼承體系中,每個類都有提供了構(gòu)造函數(shù)、復(fù)制構(gòu)造函數(shù)、和賦值操作符重載。②logic_error類及其子類、runtime_error類及其子類,它們的構(gòu)造函數(shù)是接受一個string類型的形式參數(shù),用于異常信息的描述③所有的異常類都有一個what()方法,返回constchar*類型(C風(fēng)格字符串)的值,描述異常信息。標(biāo)準(zhǔn)異常類的具體描述:異常名稱描述exception所有標(biāo)準(zhǔn)異常類的父類bad_alloc當(dāng)operatornewandoperatornew[],請求分配內(nèi)存失敗時bad_exception這是個特殊的異常,如果函數(shù)的異常拋出列表里聲明了bad_exception異常,當(dāng)函數(shù)內(nèi)部拋出了異常拋出列表中沒有的異常,這是調(diào)用的unexpected函數(shù)中若拋出異常,不論什么類型,都會被替換為bad_exception類型bad_typeid使用typeid操作符,操作一個NULL指針,而該指針是帶有虛函數(shù)的類,這時拋出bad_typeid異常bad_cast使用dynamic_cast轉(zhuǎn)換引用失敗的時候ios_base::failureio操作過程出現(xiàn)錯誤logic_error邏輯錯誤,可以在運(yùn)行前檢測的錯誤runtime_error 運(yùn)行時錯誤,僅在運(yùn)行時才可以檢測的錯誤logic_error的子類:異常名稱描述length_error試圖生成一個超出該類型最大長度的對象時,例如vector的resize操作domain_error參數(shù)的值域錯誤,主要用在數(shù)學(xué)函數(shù)中。例如使用一個負(fù)值調(diào)用只能操作非負(fù)數(shù)的函數(shù)out_of_range超出有效范圍invalid_argument參數(shù)不合適。在標(biāo)準(zhǔn)庫中,當(dāng)利用string對象構(gòu)造bitset時,而string中的字符不是’0’或’1’的時候,拋出該異常runtime_error的子類:異常名稱描述range_error計算結(jié)果超出了有意義的值域范圍overflow_error算術(shù)計算上溢underflow_error算術(shù)計算下溢invalid_argument參數(shù)不合適。在標(biāo)準(zhǔn)庫中,當(dāng)利用string對象構(gòu)造bitset時,而string中的字符不是’0’或’1’的時候,拋出該異常#include<stdexcept>classPerson{public: Person(intage){ if(age<0||age>150){ throwout_of_range("年齡應(yīng)該在0-150歲之間!"); } }public: intmAge;};intmain(){ try{ Personp(151); } catch(out_of_range&ex){ cout<<ex.what()<<endl; } system("pause"); returnEXIT_SUCCESS;}3.3.2編寫自己的異常類①標(biāo)準(zhǔn)庫中的異常是有限的;②在自己的異常類中,可以添加自己的信息。(標(biāo)準(zhǔn)庫中的異常類值允許設(shè)置一個用來描述異常的字符串)。2.如何編寫自己的異常類?①建議自己的異常類要繼承標(biāo)準(zhǔn)異常類。因為C++中可以拋出任何類型的異常,所以我們的異常類可以不繼承自標(biāo)準(zhǔn)異常,但是這樣可能會導(dǎo)致程序混亂,尤其是當(dāng)我們多人協(xié)同開發(fā)時。②當(dāng)繼承標(biāo)準(zhǔn)異常類時,應(yīng)該重載父類的what函數(shù)和虛析構(gòu)函數(shù)。③因為棧展開的過程中,要復(fù)制異常類型,那么要根據(jù)你在類中添加的成員考慮是否提供自己的復(fù)制構(gòu)造函數(shù)。//自定義異常類classMyOutOfRange:publicexception{public: MyOutOfRange(conststringerrorInfo) { this->m_Error=errorInfo; } MyOutOfRange(constchar*errorInfo) { this->m_Error=string(errorInfo); } virtual~MyOutOfRange() { } virtualconstchar*what()const { returnthis->m_Error.c_str(); } stringm_Error;};classPerson{public: Person(intage) { if(age<=0||age>150) { //拋出異常越界 //cout<<"越界"<<endl; //throwout_of_range("年齡必須在0~150之間"); //throwlength_error("長度異常"); throwMyOutOfRange(("我的異常年齡必須在0~150之間")); } else { this->m_Age=age; } } intm_Age;};voidtest01(){ try { Personp(151); } catch(out_of_range&e) { cout<<e.what()<<endl; } catch(length_error&e) { cout<<e.what()<<endl; } catch(MyOutOfRangee) { cout<<e.what()<<endl; }}4.c++輸入和輸出流4.1流的概念和流類庫的結(jié)構(gòu)程序的輸入指的是從輸入文件將數(shù)據(jù)傳送給程序,程序的輸出指的是從程序?qū)?shù)據(jù)傳送給輸出文件。C++輸入輸出包含以下三個方面的內(nèi)容:對系統(tǒng)指定的標(biāo)準(zhǔn)設(shè)備的輸入和輸出。即從鍵盤輸入數(shù)據(jù),輸出到顯示器屏幕。這種輸入輸出稱為標(biāo)準(zhǔn)的輸入輸出,簡稱標(biāo)準(zhǔn)I/O。以外存磁盤文件為對象進(jìn)行輸入和輸出,即從磁盤文件輸入數(shù)據(jù),數(shù)據(jù)輸出到磁盤文件。以外存文件為對象的輸入輸出稱為文件的輸入輸出,簡稱文件I/O。對內(nèi)存中指定的空間進(jìn)行輸入和輸出。通常指定一個字符數(shù)組作為存儲空間(實際上可以利用該空間存儲任何信息)。這種輸入和輸出稱為字符串輸入輸出,簡稱串I/O。 C++編譯系統(tǒng)提供了用于輸入輸出的iostream類庫。iostream這個單詞是由3個部分組成的,即i-o-stream,意為輸入輸出流。在iostream類庫中包含許多用于輸入輸出的類。常用的見表ios是抽象基類,由它派生出istream類和ostream類,兩個類名中第1個字母i和o分別代表輸入(input)和輸出(output)。istream類支持輸入操作,ostream類支持輸出操作,iostream類支持輸入輸出操作。iostream類是從istream類和ostream類通過多重繼承而派生的類。其繼承層次見上圖表示。C++對文件的輸入輸出需要用ifstrcam和ofstream類,兩個類名中第1個字母i和o分別代表輸入和輸出,第2個字母f代表文件(file)。ifstream支持對文件的輸入操作,ofstream支持對文件的輸出操作。類ifstream繼承了類istream,類ofstream繼承了類ostream,類fstream繼承了類iostream。見圖I/O類庫中還有其他一些類,但是對于一般用戶來說,以上這些已能滿足需要了。與iostream類庫有關(guān)的頭文件iostream類庫中不同的類的聲明被放在不同的頭文件中,用戶在自己的程序中用#include命令包含了有關(guān)的頭文件就相當(dāng)于在本程序中聲明了所需要用到的類??梢該Q—種說法:頭文件是程序與類庫的接口,iostream類庫的接口分別由不同的頭文件來實現(xiàn)。常用的有iostream
包含了對輸入輸出流進(jìn)行操作所需的基本信息。fstream
用于用戶管理的文件的I/O操作。strstream
用于字符串流I/O。stdiostream
用于混合使用C和C++的I/O機(jī)制時,例如想將C程序轉(zhuǎn)變?yōu)镃++程序。iomanip
在使用格式化I/O時應(yīng)包含此頭文件。在iostream頭文件中定義的流對象在iostream頭文件中定義的類有ios,istream,ostream,iostream,istream等。在iostream頭文件中不僅定義了有關(guān)的類,還定義了4種流對象,對象含義對應(yīng)設(shè)備對應(yīng)的類c語言中相應(yīng)的標(biāo)準(zhǔn)文件cin標(biāo)準(zhǔn)輸入流鍵盤istream_withassignstdincout標(biāo)準(zhǔn)輸出流屏幕ostream_withassignstdoutcerr標(biāo)準(zhǔn)錯誤流屏幕ostream_withassignstderrclog標(biāo)準(zhǔn)錯誤流屏幕ostream_withassignstderr在iostream頭文件中定義以上4個流對象用以下的形式(以cout為例):
ostreamcout(stdout);
在定義cout為ostream流類對象時,把標(biāo)準(zhǔn)輸出設(shè)備stdout作為參數(shù),這樣它就與標(biāo)準(zhǔn)輸出設(shè)備(顯示器)聯(lián)系起來,如果有
cout<<3;
就會在顯示器的屏幕上輸出3。在iostream頭文件中重載運(yùn)算符“<<”和“>>”本來在C++中是被定義為左位移運(yùn)算符和右位移運(yùn)算符的,由于在iostream頭文件中對它們進(jìn)行了重載,使它們能用作標(biāo)準(zhǔn)類型數(shù)據(jù)的輸入和輸出運(yùn)算符。所以,在用它們的程序中必須用#include命令把iostream包含到程序中。
#include<iostream>>>a表示將數(shù)據(jù)放入a對象中。<<a表示將a對象中存儲的數(shù)據(jù)拿出。4.2標(biāo)準(zhǔn)I/O流標(biāo)準(zhǔn)I/O對象:cin,cout,cerr,clogcout流對象cout是consoleoutput的縮寫,意為在控制臺(終端顯示器)的輸出。強(qiáng)調(diào)幾點。1) cout不是C++預(yù)定義的關(guān)鍵字,它是ostream流類的對象,在iostream中定義。顧 名思義,流是流動的數(shù)據(jù),cout流是流向顯示器的數(shù)據(jù)。cout流中的數(shù)據(jù)是用流插入 運(yùn)算符“<<”順序加入的。如果有:
cout<<"I"<<"studyC++"<<"veryhard.<<“helloworld!";按順序?qū)⒆址?I","studyC++","veryhard."插人到cout流中,cout就將它們送 到顯示器,在顯示器上輸出字符串"IstudyC++veryhard."。cout流是容納數(shù)據(jù)的載 體,它并不是一個運(yùn)算符。人們關(guān)心的是cout流中的內(nèi)容,也就是向顯示器輸出什么。
2) 用“cout<<”輸出基本類型的數(shù)據(jù)時,可以不必考慮數(shù)據(jù)是什么類型,系統(tǒng)會判斷數(shù) 據(jù)的類型,并根據(jù)其類型選擇調(diào)用與之匹配的運(yùn)算符重載函數(shù)。這個過程都是自動的, 用戶不必干預(yù)。如果在C語言中用prinf函數(shù)輸出不同類型的數(shù)據(jù),必須分別指定相應(yīng) 的輸出格式符,十分麻煩,而且容易出錯。C++的I/O機(jī)制對用戶來說,顯然是方便 而安全的。cout流在內(nèi)存中對應(yīng)開辟了一個緩沖區(qū),用來存放流中的數(shù)據(jù),當(dāng)向cout流插人一個endl時,不論緩沖區(qū)是否已滿,都立即輸出流中所有數(shù)據(jù),然后插入一個換行符,并刷新流(清空緩沖區(qū))。注意如果插人一個換行符”\n“(如cout<<a<<"\n"),則只輸出和換行,而不刷新cout流(但并不是所有編譯系統(tǒng)都體現(xiàn)出這一區(qū)別)。在iostream中只對"<<"和">>"運(yùn)算符用于標(biāo)準(zhǔn)類型數(shù)據(jù)的輸入輸出進(jìn)行了重載,但未對用戶聲明的類型數(shù)據(jù)的輸入輸出進(jìn)行重載。如果用戶聲明了新的類型,并希望用"<<"和">>"運(yùn)算符對其進(jìn)行輸入輸出,按照重運(yùn)算符重載來做。cerr流對象cerr流對象是標(biāo)準(zhǔn)錯誤流,cerr流已被指定為與顯示器關(guān)聯(lián)。cerr的作用是向標(biāo)準(zhǔn)錯誤設(shè)備(standarderrordevice)輸出有關(guān)出錯信息。cerr與標(biāo)準(zhǔn)輸出流cout的作用和用法差不多。但有一點不同:cout流通常是傳送到顯示器輸出,但也可以被重定向輸出到磁盤文件,而cerr流中的信息只能在顯示器輸出。當(dāng)調(diào)試程序時,往往不希望程序運(yùn)行時的出錯信息被送到其他文件,而要求在顯示器上及時輸出,這時應(yīng)該用cerr。cerr流中的信息是用戶根據(jù)需要指定的。clog流對象clog流對象也是標(biāo)準(zhǔn)錯誤流,它是consolelog的縮寫。它的作用和cerr相同,都是在終端顯示器上顯示出錯信息。區(qū)別:cerr是不經(jīng)過緩沖區(qū),直接向顯示器上輸出有關(guān)信息,而clog中的信息存放在緩沖區(qū)中,緩沖區(qū)滿后或遇endl時向顯示器輸出。緩沖區(qū)的概念:4.3標(biāo)準(zhǔn)輸入流標(biāo)準(zhǔn)輸入流對象cin,重點掌握的函數(shù)cin.get()//一次只能讀取一個字符cin.get(一個參數(shù))//讀一個字符cin.get(兩個參數(shù))//可以讀字符串cin.getline()cin.ignore()cin.peek()cin.putback()//cin.getvoidtest01(){#if0 charch=cin.get(); cout<<ch<<endl; cin.get(ch); cout<<ch<<endl; //鏈?zhǔn)骄幊?charchar1,char2,char3,char4; cin.get(char1).get(char2).get(char3).get(char4); cout<<char1<<""<<char2<<""<<char3<<""<<char4<<"";#endif charbuf[1024]={0}; //cin.get(buf.1024); cin.getline(buf,1024); cout<<buf;}//cin.ignorevoidtest02(){ charbuf[1024]={0}; cin.ignore(2);//忽略緩沖區(qū)當(dāng)前字符 cin.get(buf,1024); cout<<buf<<endl;}//cin.putback將數(shù)據(jù)放回緩沖區(qū)voidtest03(){ //從緩沖區(qū)取走一個字符 charch=cin.get(); cout<<"從緩沖區(qū)取走的字符:"<<ch<<endl; //將數(shù)據(jù)再放回緩沖區(qū) cin.putback(ch); charbuf[1024]={0}; cin.get(buf,1024); cout<<buf<<endl;}//cin.peek偷窺voidtest04(){ //偷窺下緩沖區(qū)的數(shù)據(jù) charch=cin.peek(); cout<<"偷窺緩沖區(qū)數(shù)據(jù):"<<ch<<endl; charbuf[1024]={0}; cin.get(buf,1024); cout<<buf<<endl;}//練習(xí)作業(yè)使用cin.get和putback完成類似功能voidtest05(){ cout<<"請輸入一個數(shù)字或者字符串:"<<endl; charch=cin.peek(); if(ch>='0'&&ch<='9'){ intnumber; cin>>number; cout<<"數(shù)字:"<<number<<endl; } else{ charbuf[64]={0}; cin.getline(buf,64); cout<<"字符串:"<<buf<<endl; }}4.4標(biāo)準(zhǔn)輸出流4.4.1字符輸出cout.flush()//刷新緩沖區(qū)Linux下有效cout.put()//向緩沖區(qū)寫字符cout.write()//從buffer中寫num個字節(jié)到當(dāng)前輸出流中。//cout.flush刷新緩沖區(qū),linux下有效voidtest01(){ cout<<"helloworld"; //刷新緩沖區(qū) cout.flush();}//cout.put輸出一個字符voidtest02(){ cout.put('a'); //鏈?zhǔn)骄幊?cout.put('h').put('e').put('l');}//cout.write輸出字符串buf,輸出多少個voidtest03(){ //char*str="helloworld!"; //cout.write(str,strlen(str)); char*str="*************"; for(inti=1;i<=strlen(str);i++){ cout.write(str,i); cout<<endl; } for(inti=strlen(str);i>0;i--){ cout.write(str,i); cout<<endl; }}4.4.2格式化輸出在輸出數(shù)據(jù)時,為簡便起見,往往不指定輸出的格式,由系統(tǒng)根據(jù)數(shù)據(jù)的類型采取默認(rèn)的格式,但有時希望數(shù)據(jù)按指定的格式輸出,如要求以十六進(jìn)制或八進(jìn)制形式輸出一個整數(shù),對輸出的小數(shù)只保留兩位小數(shù)等。有兩種方法可以達(dá)到此目的。1)使用控制符的方法;2)使用流對象的有關(guān)成員函數(shù)。使用流對象的有關(guān)成員函數(shù)通過調(diào)用流對象cout中用于控制輸出格式的成員函數(shù)來控制輸出格式。用于控制輸出格式的常用的成員函數(shù)如下:流成員函數(shù)setf和控制符setiosflags括號中的參數(shù)表示格式狀態(tài),它是通過格式標(biāo)志來指定的。格式標(biāo)志在類ios中被定義為枚舉值。因此在引用這些格式標(biāo)志時要在前面加上類名ios和域運(yùn)算符“::”。格式標(biāo)志見表13.5??刂品袷交敵鯟++提供了在輸入輸出流中使用的控制符(有的書中稱為操縱符)。//通過流成員函數(shù)voidtest01(){ intnumber=99; cout.width(20); cout.fill('*'); cout.setf(ios::left); cout.unsetf(ios::dec);//卸載十進(jìn)制 cout.setf(ios::hex); cout.setf(ios::showbase); cout.unsetf(ios::hex); cout.setf(ios::oct); cout<<number<<endl;}//使用控制符voidtest02(){ intnumber=99; cout<<setw(20) <<setfill('~') <<setiosflags(ios::showbase) <<setiosflags(ios::left) <<hex <<number <<endl;}對程序的幾點說明1)成員函數(shù)width(n)和控制符setw(n)只對其后的第一個輸出項有效。如:cout.width(6);
cout<<20<<3.14<<endl;
輸出結(jié)果為203.14在輸出第一個輸出項20時,域?qū)挒?,因此在20前面有4個空格,在輸出3.14時,width(6)已不起作用,此時按系統(tǒng)默認(rèn)的域?qū)捿敵觯ò磾?shù)據(jù)實際長度輸出)。如果要求在輸出數(shù)據(jù)時都按指定的同一域?qū)抧輸出,不能只調(diào)用一次width(n),而必須在輸出每一項前都調(diào)用一次width(n>,上面的程序中就是這樣做的。2)在表13.5中的輸出
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 餐飲業(yè)簡裝合同范本
- 2025年生態(tài)濕地恢復(fù)與綠化種植工程合同4篇
- 海鹽二手房租賃2025年度房屋維護(hù)責(zé)任合同2篇
- 2025年基礎(chǔ)設(shè)施建筑工程施工協(xié)調(diào)服務(wù)合同2篇
- 二零二五年度充電樁充電設(shè)備質(zhì)量監(jiān)督與安全評估合同4篇
- 2025版學(xué)校安保人員校園交通安全管理服務(wù)合同2篇
- 二零二四年度新能源產(chǎn)業(yè)股份轉(zhuǎn)轉(zhuǎn)讓合同模板下載3篇
- 2025年度代雇運(yùn)輸車輛特種貨物運(yùn)送服務(wù)合同4篇
- 二零二四年度專利實施許可合同書2篇
- 2025年度車輛租賃與智能交通系統(tǒng)接入合同4篇
- 中國聯(lián)合網(wǎng)絡(luò)通信有限公司招聘筆試題庫2024
- 【社會工作介入精神障礙社區(qū)康復(fù)問題探究的文獻(xiàn)綜述5800字】
- 節(jié)前停工停產(chǎn)與節(jié)后復(fù)工復(fù)產(chǎn)安全注意事項課件
- 設(shè)備管理績效考核細(xì)則
- 中國人民銀行清算總中心直屬企業(yè)2023年招聘筆試上岸歷年典型考題與考點剖析附帶答案詳解
- (正式版)SJT 11449-2024 集中空調(diào)電子計費(fèi)信息系統(tǒng)工程技術(shù)規(guī)范
- 人教版四年級上冊加減乘除四則混合運(yùn)算300題及答案
- 合成生物學(xué)技術(shù)在生物制藥中的應(yīng)用
- 消化系統(tǒng)疾病的負(fù)性情緒與心理護(hù)理
- 高考語文文學(xué)類閱讀分類訓(xùn)練:戲劇類(含答案)
- 協(xié)會監(jiān)事會工作報告大全(12篇)
評論
0/150
提交評論