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.