--------------------------------面向對象編程之繼承---------------------------------------------------------
1、定義一個類的時候想重用一個已經有的類,就可以使用繼承。
class NewClass : public OldClass{
//新增加的成員
};
新類會繼承舊類的全部成員,成為新類中的成員。新類可以增加新的成員,實現對舊類的擴展。繼承方式可以有public/private/protected三種,一般使用public方式。舊類成為“父類”或者“基類”,新類成為“子類”或者“派生類”。無論那種繼承方式都是把父類的成員全部繼承(復制)到子類中成為子類的成員,其中私有的成員在子類中不能直接訪問,公開可以在子類中直接訪問。特權是父類中可以用protected來作為成員的訪問限制,這種成員稱為保護成員。這種成員對外跟私有成員一樣,但是在子類中允許直接訪問。***盡量不應protected以免父子類之間有太強的耦合***。
不同繼承方式區別在於繼承到子類中之後作為子類的成員對外的訪問限制。private方式繼承過來後所有成員都成為子類的私有成員,protected方式繼承過來之後原來的私有成員還是私有的,原來保護的和公開的都成為子類的保護成員了;一般用public方式繼承,繼承過來的成員到子類中之後保持原有的訪問限制不變。默認是private方式。
繼承主要作用是代碼重用以及對多態提供支持。
創建對象的順序:構造函數首先調用父類的構造函數,然後按照定義順序創建成員對象,執行構造函數的函數體。析構跟構造函數相反。構造函數默認調用父類構造函數時不傳參數。如果需要傳遞參數,要在初始化列表中用父類類名來表示。***初始化列表各項排名不分先後,只是指明用什麼初始化誰***
子類定義的函數會隱藏來自父類的同名函數,即使參數表不同也不會構成重載。如果確實需要調用來自父類的那個函數,需要用“類名::”來指明。父子類之間不存在重載。
2、多重繼承:一個類可以繼承多個類,把所有父類的成員都全部繼承過來,可以為每個父類指定一個繼承方式。
繼承是一種“是一個”的關系,比如:老師是一個人。
多個父類的構造函數按繼承順序調用,與初始化列表中的順序無關。析構順序相反。
3、虛繼承:如果希望某一個類被繼承到某一級子類中時有了多份要合並只保留一份,這個類在被繼承時應該用virtual聲明為虛繼承,這個類就稱為虛基類。
虛繼承(鑽石繼承)中虛基類的構造函數由最底下合並的那個子類的構造函數直接傳遞參數。
虛基類
/ \
子類1 子類2
\ /
子類
------------------------------------------多態---------------------------------------------------------------
1、什麼是多態:對各種對象發出同一種指令時,各個對象能根據自身的情況做出相應的響應。
如果希望在調用函數時系統根據對象的真實類型去轉調用相應的函數,需要把那個函數聲明為virtual虛函數。子類中可以覆蓋這個函數,也自動成為虛函數,覆蓋(override)要求函數名和參數表都相同而且返回類型也要一致(比如父類中返回父類指針子類中可以返回子類指針)。
虛函數表:編譯器把這個類的全部虛函數的地址都保存在一張表中。每個這種類的對象裡會藏一個指針指向這個虛函數表(在構造函數裡做的),對象長度會增加4個字節。
利用多態實現類型識別:
dynamic_cast < 子類類型 & >(是父類類型的對象)把看起來是父類類型對象轉換成子類類型對象,一般用這個結果來初始化一個子類類型的對象的引用或者當場使用。成功則一切順利,失敗則拋出異常終止程序。
dynamic_cast < 子類 * >(是父類類型的對象地址)成功則結果為一個正常的子類地址,失敗則結果為NULL。常常用這一種。
dynamic_cast要求類有虛函數。
typeid關鍵字實現類型識別:
#include
typeid(類型或者數據)返回一個type_info & ,type_info裡有一個成員name()表示類型的名字,type_info支持==和!=。依賴於編譯器。如果累有虛函數,typeid也會利用多態進行類型識別。
------------------------復習:---------------------------------------------------------------------
class A{
A(int n){}
A(double d){}
A(const A& x){}
}; //拷貝構造函數
A a1(1); A a2(1.1); A a3(a1);
void func(A obj);
func(1);//func ( A(1) );
func(1.1);//func( A (1.1) );
func(a1);//A obj(a1); ==> A( A x)==>A x(a1)
--------------------------------------------------------------------------------------------------------
2、純虛函數:沒有必要或者不應該有函數體的虛函數,用“=0;”來取代函數體。有純虛函數的類稱為抽象類(缺少函數體),不允許直接用抽象類創建對象抽象類總是用來作為父類,由子類來實現(覆蓋)那些純虛函數,從而可以創建子類類型的對象,可以當成父類對象來引用,或者可以用父類指針指向子類對象。
***使用多態時必須通過父類指針或者引用來訪問子類對象,而不能重建一個父類對象***
多態也稱為動態綁定、晚綁定、運行時綁定。統一接口,便於替換和維護。
3、通過父類指針動態釋放子類對象時,默認調用父類的析構。如果希望使用多態調用對象所屬的子類的析構函數,應該在父類中把析構函數也聲明為虛函數。(可否為純虛函數?構造可否為虛函數?)
4、string類:C++風格的字符串類,< string >
構造函數:string(const string & s),string(const char * s),string(int n,char c)。
運算符:<<,>>,=,+,+=,[ ],at( int ),比較運算符<,<=,>=,>,==,!=
長度:size( ),length( ),bool empty( ),resize(newsize,fillc)
轉換成C風格:c_str( ),data( )不保證‘\0’,copy( char * to, int 字符數,int start=0)復制從start位置開始的n個字符到to所指向的地方。
字串:substr( int start , int n)返回從start開始的n個字符組成的一個字串string對象,原對象還保持不變。
追加:append( int n ,char c)在末尾追加n個字符
查找:find( char c, int start=0 )
find( const char * s, int start=0 )
find( const string & s, int start=0 )
rfind( ... )參數同上,是從反方向查找,其中可以用string : : npos表示末尾
find_first_of( 字符串 s,int start=0 ),從start位置開始查找在字符串s中包含字符。find_first_of(“+-*/”)表示在原字符串中找第一個運算符。
find_last_of( ... )
find_first_not_of(...)
find_last_not_of( ... )
找到返回下標,沒有找到返回string : : npos。
刪除:erase( int start=0, int n=string : : npos )
替換:replace( int start , int n, 新字符串)
replace( int start , int n , int n2 , char c2)
把從start位置開始的n個字符替換成新字符串或者n2個c2.
插入:insert( int pos, 新字符串)
insert( int pos, int n, char c)
行輸入:gets( buf )容易越界,fgets( buf ,siezof( buf ), stdin )保留了換行符在末尾,scanf( "%[^\n]" , buf )讀到'\n'為止,也容易越界。
string s; getline( cin , s );
3、異常處理:
什麼是異常:不常發生,但無法避免。
處理:
返回錯誤碼
設置errno全局變量
拋出異常
捕獲異常:
用try{}把可能發生異常的代碼包起來,緊跟其後用若干個(至少一個)catch(類型 e){}來捕獲指定類型的異常並處理,處理之後程序從最後一個catch塊後繼續運行。
拋出異常:throw 數據 ;
------------------------------------復習---------------------------------------------------------------------
1、多態(統一用父類引用或者指針調用虛函數),虛函數,虛表指針,動態類型識別(dynamic_cast < 子類 * > ( 父類 * ),typeid == > type_info,#< typeid >,name( ),==,!=),純虛函數=0;抽象類(不能直接用來創建對象,但可以引用或者指向已經有的子類對象),虛析構函數(用delete通過父類指針釋放子類對象時用)。string類,異常(try{throw . . .}catch(...){})
----------------------------------------------------------------------------------------------------------------
4、異常
被throw拋出異常數據不受作用范圍的限制,直到被捕獲被處理為止,因此在捕獲時可以通過引用來犯訪問異常數據。即使已經被處理的異常數據,也可以用一個不帶數據的throw重新拋出。
如果拋出的異常沒有被捕獲處理,系統會調用terminate()函數終止程序。set_terminate(func)可以指定在terminate時自動調用func函數。
異常傳遞機制:從throw開始離開正常執行流程,在本函數內找有沒有包含它的try{}塊,如果有,就依次查找它的catch塊直到第一個類型匹配為止進入這個catch塊執行處理,之後從最後一個catch塊之後繼續執行;如果沒有包含它的try{}塊或者沒有類型匹配的catch塊,異常數據繼續傳遞到上層函數(調用它的那個函數)中重復這個過程,如果直到main函數還沒有完成處理,系統就調用terminate終止程序了。
多個catch塊如果同時有父類類型和子類類型,應該把子類類型的放在前面,如果有一個是catch( . . . ),它必須放在最後。
異常聲明:一個函數可以聲明自己可能拋出哪些類型的異常,格式為:
返回類型 函數名 (參數表) throw(異常類型列表);
int func(int a[ ],int idx) throw (double, int, string);
如果在函數內部拋出了不在列表中的異常,則會引發意外。可以通過set_unexpected(func)來設置遇到意外情況時調用func。沒有異常聲明表示可能拋出任何類型的異常,空異常列表表示不會拋出異常。
標准異常:exception 類,#include< exception >
定義子類覆蓋父類的虛函數時,異常聲明的內容不能超出父類中這個函數的異常聲明內容。
標准庫中拋出的異常類型都是exception類的子類。
自己定義異常類型一般繼承exception類,覆蓋const char * what( ) const throw( )函數,返回一個字符串,描述發生了什麼異常。
4、I/O input/output
標准庫中的輸入輸出對象:
cin:控制台輸入console input (標准輸入)
cout:控制台輸出console output (標准輸出)
cerr:錯誤輸出,不緩沖,不重定向
clog:跟cerr一樣,
類體系:
| ---istringstream
|
|--- istream:cin -- |--- ifstream
| |
ios -- | |
| |---iostream --- fstream
| |
|--- ostream:cout --|--- ofstream
|
| ---ostringstream
輸入:
格式化輸入:把一系列字符轉化成對應格式的數值保存咋變量中。
ws去掉前導的空白字符(空格、制表符、換行符、回車符、換頁符、垂直制表符),hex,dec,oct表示用幾進制輸入;
cin >> ws >> hex >> n ;//65==> 0x65 ==> 101
非格式化輸入:保持原來的格式
get(),get(char &)從輸入流中讀取一個字符,空白字符也照樣取走。peek()查看但不去走,putback(char)把一個剛取
走的字符送回輸入流中(如果送回的不是剛取走的字符,系統不保證成功)。ignore(int n=1, char end=EOF)從輸入流中丟棄
n個字符,但如果提前遇到end就停止丟棄//EOF:end of file
getline(char 數組, 數組大小)讀取一行輸入,如果輸入超過最大長度,只讀去最大長度 - 1個字符並追加'\0',並設置錯誤標志。
處於錯誤狀態下的IO對象不再執行IO操作,直到clear()清除錯誤狀態為止。***注意clear只是清除錯誤狀態,並不丟棄輸入流中
的字符,想丟棄輸入流中的字符用ignore。getline(cin , string對象)則不存在超長問題。IO流對象都可以看成一個bool類型的
數據,當處於正常狀態時為真,處於錯誤狀態時為假。
while( cin >> n ){ } //輸入出錯時循環結束(不匹配,Ctrl+D)
getline可以用第三個參數指定讀到什麼字符為止。
cin.getline(buf, maxlen, end_char);
getline(cin, stringobject,end_char);
fail(),bad(),eof(),good()表示三種錯誤狀態和沒有出錯。
輸出:
格式化輸出:把內存中的數值用一系列字符表示出來,比如:12345,在內存中是0x00003039[ 39 30 00 00 ],輸出時轉換成“12345”。cout << 12345;
格式控制:setf( flag )/unsetf( flag )設置/取消標志。標志在ios中定義,使用時應該ios::標志。
boolalpha, dec/hex/oct(三選一), left/right/internal(三選一), scientific/fixed(二選一)科學計數法/小數形式, showbase顯示進制前綴(0x/0/無)showpoint顯示小數點, showpos顯示正符號, uppercase在輸出十六進制時的 x 和 a-f 以及科學計數法中的 e 用大寫字母, unibuf每次輸出後都刷新緩沖區。skipws輸入時跳過空白字符。
一般使用格式控制符取代標志。
width(w)指定下一項輸出占多少個字符的位置,如果實際數據超過指定寬度按實際數據寬度輸出而不截斷,而且寬度只對下一項有效。實際數據短則用空格填充。fill(c)可以指定用其它字符代替填充。precision(n)把小數的精度(有效數字)設置為n, 如果同時指定了fixed表示則表示小數點後的位數為n。
class wf{
int w;
char f;
public:
wf(int w, char f):w(w),f(f){}
friend ostream& operator << (ostream & o,const wf & x){
o.width(x.w);
o.fill(x.f);
return o;
}
};
cout<< wf(10,'#')<<123< 非格式化輸出:
put( c ), flush( stdout )。
---------------------------------------------------------------------------------------------------------
#include
#include
using namespace std;
istream & func(istream &i)
{
char c;
do
{
i>>c;
}while(!isdigit(c));//取走非數字字符
i.putback(c);//退回一個字符
return i;//i 就是cin
}
int main(int argc, char *argv[])
{
int n=-1;
cin>>func>>n;
cout<<"n= "< return 0;
}
-------------------------------------------------------------------------------------------------------------
1、I/O
字符串IO:把字符串當成鍵盤輸入 >> 或者當成屏幕輸出 <<。
operator <<
#include < sstream >
istringstream in( const string & str );
in >> . . .
ostringstream out;
out << . . .
out.str( ) 取得字符串
通過它們可以再 string 類型和各種其它支持 << ,>> 的類型直接轉換。
文件IO:#include < fstream >
ifstream fin 文件輸入流對象
ofstream fout 文件輸出流對象
fstream fio 文件IO對象,輸入輸出都可以
都是istream/ostream類的子類,cin/cout能做的事情,文件fin/fout都可以做。文件輸入/輸出流對象需要跟一個文件關聯。
方法1:xfstream fs(文件路徑,打開方式標志=默認值)
方法2:xfsream fs; fs.open(文件路徑,打開方式標志=默認值)
兩者都會打開文件,成功與否可以通過檢測是否為真來判斷,fs.is_open()。
標志:ios::in讀,ios::out寫,ios::app追加,ios::trunc清空,ios::ate一開始在末尾(可以調到其它地方),輸入流默認標志in,輸出流對象默認標志out|trunc。
讀寫:>>,<< ,get()/put(c),getline(),eof()判斷是否遇到了文件末尾(最後一個字節的後面)。
read(char *addr, int bytes)
write(const void * addr, int bytes)
在文件和內存之間直接按原始格式(二進制格式)轉存數據。
close()關閉文件,釋放資源,同時讓緩沖區和磁盤文件的內容同步以免丟失文件內容。
gcount()取得最後一次讀取到的字節數。
定位:
seekp/seekg(偏移量)定位到離文件開始制定偏移量的位置,0表示就在開頭。seekp/seekg(偏移量,參考基點),基點可以是ios::beg,ios::end,ios::粗人三選一。