經(jīng)典:面向?qū)ο缶幊?我的思想(下部) 轉(zhuǎn)
Posted on 2006-02-27 16:08 D調(diào) 閱讀(207) 評(píng)論(0) 編輯 收藏 所屬分類: Classics對(duì)于上面的實(shí)例,它已經(jīng)能完成絕大部分工作了,但它還是不完善的,還有許許多多的細(xì)節(jié)等到我們?nèi)ネ晟疲∫苍S有的同學(xué)已經(jīng)注意到了,當(dāng)我創(chuàng)建完“jingwei”這個(gè)對(duì)象時(shí),這個(gè)對(duì)象的所有的屬性都是空的,也就是說:這個(gè)對(duì)象的姓名是未定的、年齡是未定的、性別是未定的、薪水是未定的、午餐也是未定的。而我們想把這些屬性都添加上去,就還要用對(duì)象調(diào)用相應(yīng)的方法,去一個(gè)個(gè)修改!天啊,這簡(jiǎn)直是太麻煩了!有沒有什么好方法能夠在我們創(chuàng)建對(duì)象的同時(shí)就完成了對(duì)屬性賦值的操作呢?哦不,應(yīng)該說是對(duì)屬性的初始化呢?當(dāng)然沒問題了,這就需要所謂的構(gòu)造函數(shù)!
構(gòu)造函數(shù)是類中最特殊的函數(shù),它與析構(gòu)函數(shù)的功能正好相反!
從特征上來說:1.它是編程語言中唯一沒有返回值類型的函數(shù)。
2.它的名稱與類的名稱必須要完全相同。
3.它必須被聲明為公共(public)的類型
4,可以對(duì)構(gòu)造函數(shù)進(jìn)行重載。
5.它在創(chuàng)建對(duì)象是自動(dòng)被調(diào)用。
從功能上來說:1.它是對(duì)類中的屬性進(jìn)行初始化。
其實(shí),對(duì)于上面的程序來說我們沒有自己定義構(gòu)造函數(shù)。但是,在這種情況下,系統(tǒng)會(huì)自動(dòng)為我們定義一個(gè)“默認(rèn)構(gòu)造函數(shù)”。他會(huì)把數(shù)值變量自動(dòng)賦值為0,把布爾行變量賦值為false等等(但在C++中,默認(rèn)構(gòu)造函數(shù)不初始化其成員)。如果程序員定義了構(gòu)造函數(shù),那么系統(tǒng)就不會(huì)再為你的程序添加一個(gè)缺默認(rèn)造函數(shù)了。(在這里,我們提倡的是自己定義構(gòu)造函數(shù),而不是用系統(tǒng)的默認(rèn)構(gòu)造函數(shù))
還是看個(gè)實(shí)例吧!這樣比較清楚一些!
//employee.java |
這樣,在我們創(chuàng)建“jingwei”這個(gè)對(duì)象的同時(shí),它的所有的屬性也被初始化了!顯然,這大大的提高了工作效率,但是,它還是不符合要求。想想看,如果我們現(xiàn)在創(chuàng)建這個(gè)類型的第二個(gè)對(duì)象的時(shí)候會(huì)發(fā)生什么事情?告訴你,除了對(duì)象的“名”(這個(gè)名稱不在是對(duì)象屬性中的名稱,而是對(duì)象本身的名稱)不一樣外,其所有的“屬性值”都一樣!比如:現(xiàn)在我們創(chuàng)建第二個(gè)對(duì)象flashmagic,然而我會(huì)發(fā)現(xiàn)這個(gè)對(duì)象的所有的屬性和jingwei這個(gè)對(duì)象的所有的屬性完全相同。而我們只能在用對(duì)象的方法去改變著寫屬性了!很顯然,這種方法不大好!我們需要一種方法在創(chuàng)建對(duì)象的時(shí)候?yàn)閷?duì)象的屬性賦予“我們想要的值”。
相信你也看到了,默認(rèn)構(gòu)造函數(shù)就顯得無能為力了。我們需要的是帶參數(shù)的構(gòu)造函數(shù),在創(chuàng)建對(duì)象時(shí),我們把參數(shù)傳給構(gòu)造函數(shù),這樣就能完成了上述的功能!口說無憑,還是來看個(gè)實(shí)例吧:
//employee.java |
這樣一來,在創(chuàng)建對(duì)象的同時(shí)我們就可以給他賦予我們想要的值,很顯然,這可就方便多了。哦,對(duì)了!還沒有告訴你怎么創(chuàng)建呢!哈哈,往前翻幾頁你會(huì)看到這句話:
jingwei = new employee();這是創(chuàng)建一個(gè)對(duì)象,而我們把它改成
jingwei = new employee("jingwei",20,'M',100,false);這樣一來,所有的工作都完成了,呵呵!(在創(chuàng)建對(duì)象的同時(shí)賦予了我們想要的“初值”)
2.3.2重載構(gòu)造函數(shù):
我還是先把概念給你吧,讓你有個(gè)認(rèn)識(shí),隨后我們?cè)谶M(jìn)行論述。
在JAVA中:
1. 函數(shù)重載是一個(gè)類中聲明了多個(gè)同名的方法,但有不同的參數(shù)個(gè)數(shù)和參數(shù)類型。
2. 函數(shù)重構(gòu)是指在子類中聲明與父類同名的方法,從而覆蓋了父類的方法。重構(gòu)解決了子類與父類的差異問題。(在討論到繼承時(shí)我會(huì)詳細(xì)說明)
在C++中:
1. 函數(shù)重載的概念一樣。
2. 重構(gòu)的概念可就不一樣了,C++中功能更為龐大的虛函數(shù)。更詳細(xì)內(nèi)容這里就不錯(cuò)過多介紹了!
其實(shí)關(guān)于重載的概念你并不陌生,在編程中相信你也接觸過。呵呵!讓我們來舉個(gè)操作符重載的例子你就會(huì)明白了,(JAVA中不支持這個(gè)功能)我們定義三個(gè)整數(shù)變量:
int i1=2, i2=3,i3=0;
i3 = i1 + i2;
此時(shí)i3=5;加號(hào)實(shí)現(xiàn)了兩個(gè)數(shù)相加的運(yùn)算功能。然而我們現(xiàn)在要定義三個(gè)字符串變量:
String str1=”jing”, str2=”wei”,str3=””;
str3 = str1 + str2;
此時(shí)str3 = “jingwei”;加號(hào)實(shí)現(xiàn)了兩個(gè)字符串相加的運(yùn)算功能。同樣是加號(hào),既可以把兩個(gè)整型的變量加在一起,也可以把兩個(gè)字符串類型的變量加在一起。同一個(gè)操作符實(shí)現(xiàn)了不同的功能------這就是所謂的操作符重載(嘿嘿,我說你一定見過吧:)!不就好像是漢語中的一詞多意一樣!我需要說明一下的是,C++中的操作符重載可沒有這么簡(jiǎn)單。比如,我們可以對(duì)兩個(gè)自定義類型的對(duì)象進(jìn)行相加的運(yùn)算,進(jìn)行賦值的運(yùn)算。這樣書寫簡(jiǎn)潔明了,而且非常實(shí)用。當(dāng)然,關(guān)于操作符重載的話題太多了,有興趣再看看書吧!
我們把操作符的話題在轉(zhuǎn)到函數(shù)上來,我們一直強(qiáng)調(diào)的是“對(duì)象調(diào)方法”------對(duì)象其實(shí)調(diào)的是方法的“名稱”。而我們現(xiàn)在要對(duì)方法進(jìn)想重載,也就是定義多個(gè)相同名稱的函數(shù),這樣計(jì)算機(jī)在調(diào)用的時(shí)候不會(huì)混淆嘛?我想應(yīng)該不會(huì)的,呵呵,因?yàn)閮H僅是函數(shù)名稱相同,而我們?cè)谡{(diào)用函數(shù)時(shí)會(huì)把參數(shù)傳遞給他的。既是沒有參數(shù)也是一種參數(shù)傳遞參數(shù)的信息(信息為無參數(shù))!然而由于參數(shù)類型、參數(shù)數(shù)量、返回值類型不同我們就可以對(duì)相同名稱的函數(shù)進(jìn)行區(qū)分了!目的只有一個(gè),用簡(jiǎn)便的方法實(shí)現(xiàn)更多的功能。還是舉個(gè)例子吧,重載構(gòu)造函數(shù)!
public class employee{ |
看,在一個(gè)類中有兩個(gè)名稱相同的函數(shù),可我們?cè)谑褂玫臅r(shí)候系統(tǒng)如何知道我們調(diào)用的是那個(gè)版本的函數(shù)呢?呵呵,我剛剛說過了,可以通過函數(shù)的參數(shù)類型、參數(shù)數(shù)量、返回值類型來確定。現(xiàn)在我們接著試驗(yàn),我們創(chuàng)建兩個(gè)對(duì)象其中的一個(gè)調(diào)用帶參數(shù)的構(gòu)造函數(shù),第二個(gè)對(duì)象調(diào)用缺省值的構(gòu)造函數(shù)。我們來看看結(jié)果:
jingwei = new employee("jingwei",20,'M',100,false);/*創(chuàng)建這個(gè)對(duì)象的時(shí)候調(diào)用的是帶參數(shù)的構(gòu)造函數(shù)*/
flashmagic = new employee();//創(chuàng)建這個(gè)對(duì)象是調(diào)用的是卻省值的構(gòu)造函數(shù)
而得到的結(jié)果呢?讓我們一起來看一看!
Jingwei這個(gè)對(duì)象中: flashmagic這個(gè)對(duì)象中:
name jingwei name jw age 20 age 20 |
看,雖然是兩個(gè)名稱完全相同的函數(shù),但完成了不同的工作內(nèi)容。呵呵!關(guān)于函數(shù)重載我們就料到這里吧,我相信你已經(jīng)有個(gè)大印象了,而更詳細(xì)的內(nèi)容仍需要你的努力!
重載普通的函數(shù)與重載構(gòu)造函數(shù)大同小異,不過他多了一個(gè)this指針!this一般是對(duì)當(dāng)前對(duì)象的引用。這么說吧,如果涉及到兩個(gè)以上的對(duì)象時(shí)就會(huì)使用this指針。每個(gè)成員函數(shù)都有一個(gè)this指針,它是一個(gè)隱藏的參數(shù),this指針只向調(diào)用它的對(duì)象!我說過方法只有一份,而對(duì)象都有自己的屬性,當(dāng)對(duì)象調(diào)用方法來先是屬性的時(shí)候,他怎么來判斷調(diào)用的時(shí)不是自己的屬性呢?這就需要this指針來大顯神威了。
關(guān)于拷貝構(gòu)造函數(shù)、內(nèi)聯(lián)函數(shù)、虛函數(shù)、模版等歐就不做過多的討論了,因?yàn)?/SPAN>JAVA中好像沒有這些了。不過我需要提醒你一下的是,在C++中,類內(nèi)定義的函數(shù)自動(dòng)轉(zhuǎn)換為內(nèi)聯(lián)函數(shù),而這好像與我前面提到的思想有沖突。因?yàn)閮?nèi)聯(lián)函數(shù)的目的是減少函數(shù)調(diào)用的開銷!呵呵!我也沒繞出來呢!還請(qǐng)哪為大蝦指點(diǎn)一二!謝!
2.3.3 初始化與賦值
這里我卻要提醒你一下的是,初始化與賦值是完全不同的兩個(gè)概念。創(chuàng)建一個(gè)類的時(shí)候會(huì)調(diào)用這個(gè)類的構(gòu)造函數(shù)對(duì)對(duì)象的屬性進(jìn)行初始化。而如果以后再把這個(gè)對(duì)象賦給其他同類型的對(duì)象時(shí)可就沒那么簡(jiǎn)單了。在JAVA中直接賦值就行了,因?yàn)?/SPAN>JAVA中取消了指針,不存在指針的深拷貝與前拷貝問題。而在C++中就需要拷貝構(gòu)造函數(shù)以及操作符重載了。因?yàn)?/SPAN>JAVA中不牽扯這些東西,所以偶就不做過多介紹了。詳情請(qǐng)參閱相關(guān)書籍吧!
2.3.4析夠函數(shù):
JAVA中不再支持指針了,所以你感覺不到它的重要性,因?yàn)橄到y(tǒng)會(huì)自動(dòng)為你釋放內(nèi)存。而在C++中一切都是手動(dòng)的。在構(gòu)造函數(shù)中new了一個(gè)指針,在析夠函數(shù)中就要delete這個(gè)指針。
2.3.5靜態(tài):
現(xiàn)在我們?cè)賮砜匆豢?/SPAN>“靜態(tài)”是咋一回事兒!
把一個(gè)變量或函數(shù)聲明為靜態(tài)的需要“static”這個(gè)關(guān)鍵字。聲明靜態(tài)的目的是“為某個(gè)類的所有對(duì)象的某個(gè)屬性或方法分配單一的存儲(chǔ)空間”。靜態(tài)的數(shù)據(jù)是屬于類的,不屬于任何的對(duì)象。靜態(tài)的數(shù)據(jù)在聲明的時(shí)候系統(tǒng)就為他分配了內(nèi)存空間,而不用等到創(chuàng)建對(duì)象時(shí)。舉個(gè)例子來幫你更好的理解它吧。
還是接著上面的例子。還記得剛剛我說過的員工能用微波爐熱飯的事情吧。現(xiàn)在我們要找一個(gè)手套,畢竟想把熱好的飯從微波爐里拿出來直接下手是不行的。我把手套定義成一個(gè)布爾型的變量,它有干凈和臟兩種狀態(tài)。想想看手套是屬于誰的?所有對(duì)象?不對(duì)!因?yàn)橹挥蟹椒ú拍軐儆谒械膶?duì)象。它是屬于類的,它像微波爐那個(gè)方法一樣,在內(nèi)存中只有一份,所有的對(duì)象通過方法都能夠修改它。而下一次修改是基于上一次修改的基礎(chǔ)之上的!我的意思是:一個(gè)員工把手套弄臟了,下一個(gè)員工在使用的時(shí)候它還是臟的。而這個(gè)員工把手套洗干凈之后,別人再用的時(shí)候它就是干凈的了!就這么點(diǎn)事兒,明白了吧!
關(guān)于靜態(tài)函數(shù)我想就沒什么可多說的了。給我的感覺就是,它也是屬于類的,在定義的時(shí)候就分配的內(nèi)存。調(diào)用是可以使用類名直接調(diào)用。其他的和普通成員函數(shù)沒什么不同的了不過這里需要說明的一點(diǎn)是:在JAVA中,靜態(tài)的成員函數(shù)只能修改靜態(tài)的屬性,而靜態(tài)的屬性可以被所有的成員函數(shù)修改。不過在C++中就沒這么多事兒了!
2.4繼承
繼承很好理解,它的最大好處就是“代碼重用”,大大提高了工作效率。舉個(gè)例子你就明白了。世界上先有的黑白電視機(jī),它有自己的工作原理。然而人們?cè)谒幕A(chǔ)之上開發(fā)出了彩色電視機(jī)。彩色電視機(jī)繼承了黑白電視機(jī)的所有的特性與方法!因?yàn)樗饶茱@示彩色圖像也能顯示黑白圖像。然而它與黑白電視機(jī)又有許多區(qū)別,在工作原理上。彩色電視及多了矩陣色電路,把彩色信號(hào)分離出三種顏色(RGB),他就能顯示彩色的圖像了。而黑白電視機(jī)沒有這塊電路,即使它收到了彩色信號(hào)也顯示不了彩色圖像。彩色電視機(jī)是從黑白電視機(jī)中派生出來的。所以,黑白電視機(jī)是父類,彩色電視既是子類,彩色電視繼承了黑白電視機(jī)所有的特性與方法。看看再計(jì)算機(jī)中它是什么樣子的吧:
//BWtv.java 父類的定義 public changeBWtv(int i){ |
有了上面的定義,我們來看看他們都有什么數(shù)據(jù)。
BWtv的數(shù)據(jù)包括 Ctv的數(shù)據(jù)包括
private int a private int a
private int b
public changeBWtv(); public changeBWtv()
public changeCtv();
你看,子類擁有父類的所有的方法及屬性。注意關(guān)鍵字”extends”,它的意思是繼承。在C++中使用的是“:”操作符。意思是一樣的。但是這里有許多問題,首先是訪問權(quán)限的問題,子類的對(duì)象擁有父類的所有的屬性和方法這句話。對(duì)嘛?肯定是對(duì)的!(不過JAVA的書中可不是這么說的,他說只繼承非private類型的屬性及方法,我覺得它這句話有錯(cuò)誤!)可是,子類的對(duì)象不能直接訪問父類的私有屬性或方法,它只能通過父類的公有成員函數(shù)來訪問。而此時(shí),如果你修改了父類的屬性的值。那就真的修改了。我的意思是:父類的私有屬性的值會(huì)隨著子類對(duì)象調(diào)用父類的公有方法進(jìn)行對(duì)相應(yīng)屬性的修改而發(fā)生變化!(這里面存在一個(gè)域的問題,所有的修改都是在子類中進(jìn)行的,修改的是子類繼承的父類的屬性(在子類這個(gè)域中,此時(shí)父類以拷貝到子類中了。)。而程序中定義的父類的屬性不會(huì)發(fā)生任何變化(在父類的域中),)
其次是構(gòu)造函數(shù),在創(chuàng)建一個(gè)子類對(duì)象時(shí)首先要調(diào)用的是父類的構(gòu)造函數(shù),然后再調(diào)用子類的構(gòu)造函數(shù),畢竟,子類的構(gòu)造函數(shù)不包括父類的屬性的初始化功能!(從這一點(diǎn)來說我的觀點(diǎn)又是正確的“子類的對(duì)象擁有父類的所有的屬性和方法”)當(dāng)然了,析夠函數(shù)的調(diào)用順序正好相反!
現(xiàn)在讓我們來談?wù)?/SPAN>protected這個(gè)關(guān)鍵字吧,它的意思是:對(duì)對(duì)象來說,聲明為protected的變量是私有的,而對(duì)子類父類來說,聲明為protected的變量是公共的。
現(xiàn)在又出現(xiàn)了這樣的一個(gè)問題,如果我們?cè)谧宇愔幸捕x了一個(gè)int 類型的變量a,那我們?cè)趧?chuàng)建子類的對(duì)象的時(shí)候調(diào)用的是子類定義的還是父類定義的呢?這就涉及到數(shù)據(jù)的隱藏的問題了,我可以告訴你肯定是調(diào)用的子類的變量a。因?yàn)椋宇惏迅割惖倪@個(gè)同名變量給隱藏了。而如果是方法呢?這就涉及到重構(gòu)的問題了,在上面我提到過“函數(shù)重構(gòu)是指在子類中聲明與父類同名的方法,從而覆蓋了父類的方法。重構(gòu)解決了子類與父類的差異問題。”這里必須要聲明一下的是,在JAVA中,子類出現(xiàn)了對(duì)父類屬性的隱藏和父類方法的覆蓋后,在子類中,子類對(duì)象僅能調(diào)用子類本身的屬性和方法。要調(diào)用父類的屬性和方法必須要實(shí)用super這個(gè)關(guān)鍵子。而在C++中就不這樣了。因?yàn)樗刑摵瘮?shù)
虛擬函數(shù)在C++中非常好玩的事。我們可以把需要改寫的函數(shù)聲明為虛函數(shù),用virtual這個(gè)關(guān)鍵字來聲明。這樣。假如如果我們CwinApp這么一個(gè)基類,它里面定義了一個(gè)成員(虛)函數(shù)為InitInstance()和另一個(gè)為(虛)函數(shù)InitApplication()。如果我從CWinApp派生一個(gè)子類為CMyWinApp并修改了InitInstance()這個(gè)成員函數(shù)。我們并沒有修改InitApplication()這個(gè)成員函數(shù)。現(xiàn)在我們創(chuàng)建CMyWinApp這個(gè)類的函數(shù)theApp,我們并創(chuàng)建一個(gè)指針*pApp指向這個(gè)對(duì)象theApp。此時(shí):
pApp->InitInstance() //指針調(diào)用的是子類CMyWinApp的虛方法
pApp->InitApplication() //指針調(diào)用的時(shí)父類CwinApp的虛方法
因?yàn)樽宇惒]有修改父類的方法,所以調(diào)用的是父類的虛方法。這就牽扯到虛你表的問題。礙與本篇文章的定位,這里就不討論了!
關(guān)于父類與子類的對(duì)象的類型轉(zhuǎn)換問題是這樣的,子類對(duì)象轉(zhuǎn)換為父類對(duì)象時(shí),不會(huì)出現(xiàn)錯(cuò)誤。因?yàn)樽宇惏割惖乃械膶傩约胺椒ǎ割愊蜃宇愞D(zhuǎn)換時(shí)就難說了,呵呵。這還會(huì)牽扯到虛擬表的問題,也不討論了!
JAVA中不再支持多重繼承,也就是一個(gè)類從兩個(gè)以上的類中繼承而來,但它卻多了接口的概念“interface”。這里就不做過多介紹了!
關(guān)于抽象基類也沒什么難的!他的一個(gè)大概念就是:做為許多類的父類,不定義對(duì)象,只做派生用!
我能做得也只有這些了,如果你能明白以上的六七成,那就是對(duì)我最大的回報(bào)了,呵呵!就像剛剛開始我說的,我只是給你一個(gè)大概的思想,至于內(nèi)部的實(shí)現(xiàn)細(xì)節(jié),仍需要你的繼續(xù)努力。關(guān)于編程語言的內(nèi)容還有許多許多,實(shí)屬小生個(gè)人能力有限而不能全盤照顧到。不過作為一個(gè)初學(xué)者的你來說,這些東西都是基本的。需要我提醒你一點(diǎn)的是,不要指望在第一、二遍的時(shí)候看懂什么!加油:)
04.4.29 韓景維
愿意和大家保持聯(lián)絡(luò),如果大家能夠?qū)Ρ酒恼绿岢鰧氋F的意見和建議,我將不勝感激。我的電子郵件地址是:
onegenius@126.com
或在QQ里給我留言(86228551---亂碼游魂.h)