精華區beta C_and_CPP 關於我們 聯絡資訊
C++物件導向程式語言簡明教程之二 <第二章 概論 II > 撰文:程式創作區 小樹 使用編譯器:Turbo C++ 3.0 修改:lcr(小牛) 修改前言: 這篇小樹寫的 oop 概念蠻容易懂的, 蠻適合初學者看一下.. 只是筆者覺得寫的有點亂亂的 (^_^), 所以小弟在這整理了一下, 並沒有修改內容....請小樹哥哥見諒啦....:p Lcr (1996.10.13) 讀者須知: 本文章由C 開始談起, 算是一篇C++的入門參考書, 文內假設各位已經有 C 語言的基礎,是C++的初學者, 讀者至少必須對C有相當程度的熟悉, 如流程 控制(if,for,while,switch...),指標,陣列,結構(struct), 都必須非常熟悉 , 否則恐怕會有閱讀上的困難.... 本文件的目的是想要讓C++更好學, 使大家都能輕易的瞭解C++的精義。 因此本文件歡迎傳閱, 若有需要也歡迎做適當的修改, 以修正錯誤或增加可 讀性. 但請勿加入不雅的言語, 而且修改者務必註明修改者名稱及修改日期, 並保留原作者之簽名. 謝謝各位! <1> 再談類別繼承 讓我們來繼續談上一回的繼承. 在繼承關係中, 一個類別的上一層稱為此 類別之父類別, 一個類別的下一層稱為此類別之子類別或衍生類別. 例如生物 類別為動物類別之父類別, 動物類別為生物類別之子類別. C++有一個規定:"一個父類別的指標可以指向一個他的子類別" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 請看: class father { }; class son:public father { }; void main() { father *man; son peter; man=&peter; } 以上的敘述完全合法, 不會有任何錯誤. 至於有什麼用途, 等我們真正開 始做程式時你就會明瞭! <2> 虛擬函式 我們改造一下上一個例子: class father { public: DoJob(); //做工作 }; class son:public father { public: DoJob(); //做工作 }; void main() { father *man; //父親類別指標. son peter; man=&peter; //父親的指標可以指向兒子,這是上一章才說過的. man->DoJob(); //到底是爸爸的DoJob會被呼叫呢?還是兒子的? } 在上例中,爸爸和兒子都多了一個成員函式--DoJob(), 意思是叫他們作自己 的工作的意思!! 由於爸爸的工作和兒子不同, 所以內容當然不一樣啦!! 以上面 的寫法, 雖然man是指到peter, man->Dojob()應該要執行的是兒子的工作, 但是 程式到底會叫兒子執行爸爸的工作呢? 而是作自己的工作呢? 其實程式的本意當然是希望兒子執行他自己的工作啦!!(因為man 指到的是peter 啊!!) 可是照上面的寫法,C++卻會執行爸爸的工作..要讓程式能執行指標所指到 的人的函式, 程式必須這麼寫: class father { public: virtual DoJob(); //做工作 }; class son:public father { public: virtual DoJob(); //做工作 }; void main() { father *man; son peter; man=&peter; man->DoJob();//因為指標指到的是兒子,所以兒子做工作 } 上面的程式在函式前多加了"virtual"敘述, 稱作虛擬函式, 加上去之後,程 式便會根據指標所指的人,去執行他該做的事, 這樣瞭解嗎? 虛擬函式又叫動態連結,或晚連結, 因為到底要執行誰的函式要等到實際執行 才知道. <3> 參考型別 在C++中,提供了一個C 所沒有的特性--"參考型別" 其實參考型別沒有什麼, 他只是替物件建立一個別名罷了! 請看: int i=5; //i 是個int 物件. int &j=i; //j 是 i 的別名,即j 是i 的參考,明白的說, //i 和 j 是同一個變數的不同名字. i++; //執行後 i 變成 6. j++; //因為j 就是i ,故i 此時為 7. 所謂的"參考",其實就是別名. 就好像蔣中正又叫蔣介石一樣. 其實j 和i 是 同一個變數, 只是名字不同罷了!! 這樣懂乎? 其實如果只有這樣, 那參考還真是沒什麼用處! 事實上參考最大的用處,在於 函式傳參數時使用. 請看下例: struct test //一個結構 { int string[1000]; }; void function(test value) //此函式會印出value.string { cout << value.string; } void main() { test a; //建立一個a物件 function(a); //將a傳給函式 } test結構是一個很大的結構, 光是a 這個物件就至少要佔掉2000 bytes的記 憶體, 各位想想, 當我將a 傳給function 的時候, 因為是傳值, 所以C++會先做 出一個test結構的 value物件, 然後再將a 一個位元一個位元的copy 給value, 天啊! 這既佔記憶體又耗時間, 如果我能直接將a 拿給function函式使用的話多 好啊!! 用傳參考就可以做到這一點, 而且不會像傳址那麼彆扭, 請看改進後的程 式碼: struct test //一個結構 { int string[1000]; }; void function(test & value) //傳參考,這裡多了一個 & 符號 { cout << value.string; } void main() { test a; //建立一個a物件 function(a); //將a傳給函式,注意!!函式用法不變喔!! } 只要照上面這樣寫, value 就會是a 的別名, 也就是說value 就是 a啦!!! ( 但是生存期跟傳值時一樣) 不必多佔記憶體, 也不必花copy物件的時間. 這不是很 好嗎!! 參考的用處就在此, 這樣瞭解嗎? <4> const 物件 相信熟悉C 語言的你, 對"#define"這個關鍵字應該很熟了吧!! 使用#define 可以在C語言中定義出常數, 例: #define PI 3.1416159 //定義常數PI void main() { cout << PI; //印出PI } 可是#define使用在C++中卻有缺陷, (不過這個缺陷不是重點,為省各位的腦力 我就不講啦!!) 為此C++提出了新的定義常數的方法---const 物件!! 請看: const double PI=3.1416159; void main() { cout << PI; //印出PI PI=10; //錯誤!!!const 物件不能被更改 } 所謂的const 物件, 其實他和一般的物件一樣, 但是他在定義後就不能改值了, 而且在定義時就必須要給初值,(不給不行喔!!) 若嘗試著去更動const物件, 則編譯 器會給予警告, 並終止編譯!! <5> 行內函式 相信各位C 的高手們對巨集函式應該不陌生吧!! 巨集函式有執行速度快的優點, 可是C 的巨集函數卻是很麻煩, 一個不小心就會留下難以除錯的BUG!! C++提供了行內函式, 用以取代傳統C 之巨集!! 其實行內函式很簡單, 只要在 函式名字的前面加上"inline"的識別字, C++就會視情況將行內函式拓展成巨集, 請 看: inline int max(int a,int b) { if(a>b) return a; else return b; } 如此一來,max就是一個快速的巨集函式了!! 值得注意的是: 行內函式為巨集, 故不可遞迴, 即不可呼叫自己. 現在讓我們來回溯一下我們第一章所建立的子彈類別: ┌──────────────────────────────────┐ │class _Bullet//這是一個 "子彈" 的 "模子",改用class來寫 │ │{ │ │ private: //私用成員 │ │ int x; │ │ int y; │ │ void show(int x,int y); //這是一個函式,可以在(x,y)上畫 │ │ public: //公用成員 出一點. │ │ _Bullet(){x=y=0;} //無參數之建構函式. │ │ _Bullet(int X,int Y){x=X;y=Y;} //有兩個參數之建構函式. │ │ void go() //這是一個函式,呼叫他可以讓子彈│ │ { //往下移一格並畫出來. │ │ show(x,y); │ │ y++; │ │ } │ │ int collision(int X,int Y) //這是一個函式,用來判斷子彈是 │ │ { //否撞到東西. │ │ return (x==X && Y==y); //不是則傳回0 │ │ } │ │}; │ └──────────────────────────────────┘ 在這個類別中,類別內函式成員都是定義在類別中,(注意!!定義和宣告是不 一樣的!!) 其實我們也可以定義在類別外的, 請看: ┌──────────────────────────────────┐ │class _Bullet │ │{ │ │ private: │ │ int x; │ │ int y; │ │ void show(int x,int y); │ │ public: │ │ _Bullet(); //只宣告不定義 │ │ _Bullet(int X,int Y); │ │ void go(); │ │ int collision(int X,int Y); │ │}; │ │ │ │Bullet::_Bullet() //定義在外的建構函式 │ │{ │ │ x=y=0; │ │} │ │ │ │_Bullet::_Bullet(int X,int Y) //同上 │ │{ │ │ x=X; │ │ y=Y; │ │} │ │ │ │void _Bullet::go() //同上 │ │{ │ │ show(x,y); │ │ y++; │ │} │ │ │ │int _Bullet::collision(int X,int Y) //同上 │ │{ │ │ return (x==X && Y==y); │ │} │ └──────────────────────────────────┘ 一個類別的函式成員若過大, 定義在外面是比較好的, 因為這樣子好看多了!! 相信從上例大家應該可以學會類別內函式定義在外的語法!! 若要將類別的函式成 員定義在外, 其語法為: 函式傳回值 所屬類別::函式名(參數) { ...... } 基本上,對於定義在內的成員,C++會將其編碼為行內函式, 定義在外的則編碼 為一般函式!! 若要定義在外的也為行內函式, 只要加上inline敘述即可!! <第二章結束> 小樹 1996 8/16 -- ※ 發信站: 批踢踢實業坊(ptt.twbbs.org) ◆ From: cherry.cs.nccu.