摘要:很多高校都采用C++語言講解“面向?qū)ο蟪绦蛟O(shè)計”課程。本文結(jié)合實例探討了在學(xué)習(xí)面向?qū)ο蠹夹g(shù)之前應(yīng)該做好的兩個方面的準(zhǔn)備工作,并從多個角度對這兩個方面做了詳細的論述。
關(guān)鍵詞:過程化;面向?qū)ο?;范型;?/p>
中圖分類號:G642文獻標(biāo)識碼:A
文章編號:1672-5913(2007)14-0041-04
1引言
面向?qū)ο蟪绦蛟O(shè)計是計算機科學(xué)專業(yè)的核心課程,理論性與實踐性并重。很多學(xué)校采用C++語言為范例講述面向?qū)ο蟪绦蛟O(shè)計這門課程。教學(xué)內(nèi)容主要包括面向?qū)ο蟪绦蛟O(shè)計范型(paradigm)的三個最重要的特性:繼承、封裝和多態(tài),以及C++如何實現(xiàn)這三個特性,使學(xué)生掌握面向?qū)ο蟪绦蛟O(shè)計范型和一種面向?qū)ο蟮木幊陶Z言,從而為以后的學(xué)習(xí)打下基礎(chǔ)。但是,由于存在從過程化編程到面向?qū)ο缶幊趟枷朕D(zhuǎn)變的突然性和C++語言實現(xiàn)面向?qū)ο髾C制的復(fù)雜性,所以在開始講授面向?qū)ο蟛糠种白龊贸浞值臏?zhǔn)備工作,掌握必要的預(yù)備知識是非常重要的,否則學(xué)生就會陷入細節(jié)的困惑而影響對面向?qū)ο缶幊虣C制的理解,影響了教學(xué)效果,本文從教學(xué)實踐經(jīng)驗出發(fā),根據(jù)實際教學(xué)體會,試圖在這一方面做進一步探索,結(jié)合實例探討在講授C++語言描述的面向?qū)ο蟪绦蛟O(shè)計課程之前應(yīng)該掌握的預(yù)備知識及其必要性。
2面向?qū)ο蟪绦蛟O(shè)計的教學(xué)現(xiàn)狀
在很多高等院校中,講授面向?qū)ο蟪绦蛟O(shè)計這一課程時都是使用C++語言描述,因為在大學(xué)早期一般都學(xué)習(xí)過C語言,所以使用C++語言描述可以使學(xué)生在學(xué)習(xí)過程中有銜接感,不至于在基本語法上都感到陌生。正是有了C語言的基礎(chǔ),所以很容易認為不用做什么準(zhǔn)備或者做很少的準(zhǔn)備就可以開始面向?qū)ο蟛糠值闹v述。但是存在三個問題:首先,概念上很難轉(zhuǎn)變。C語言是過程化的語言,關(guān)注的是功能的劃分和函數(shù)的實現(xiàn),是一種和面向?qū)ο笸耆煌木幊谭缎?。如果一開始就講授類、對象等面向?qū)ο笾R,沒有一個合理的鋪墊,會有很強的突兀感,不易接受;其次,C++是一種靈活強大的語言,即使在過程化編程部分也有一些與C語言不同的地方,而這些地方卻往往是C++語言實現(xiàn)面向?qū)ο髾C制的重要部分,如果沒有一個清晰明確的認識,只知其然而不知其所以然,就難以真正掌握C++語言的面向?qū)ο蟛糠?;再次,講授C語言時重點在于編程的基本概念和程序結(jié)構(gòu)的控制,指針和結(jié)構(gòu)體并不是重點的內(nèi)容,或者說這些內(nèi)容剛開始很難完全掌握,而這部分內(nèi)容在講解面向?qū)ο筮^程中會經(jīng)常使用,如果不進行回顧就直接在面向?qū)ο蟮慕虒W(xué)中使用也會影響教學(xué)效果。所以在學(xué)習(xí)面向?qū)ο蟪绦蛟O(shè)計之前做好充分的概念和技術(shù)上的準(zhǔn)備是很重要的,起到了承上啟下的作用。下面就對學(xué)習(xí)面向?qū)ο蟪绦蛟O(shè)計之前應(yīng)該準(zhǔn)備的相關(guān)知識逐個做一討論。
3應(yīng)該掌握的預(yù)備知識
在開始學(xué)習(xí)面向?qū)ο蟪绦蛟O(shè)計之前所要加強或者全新學(xué)習(xí)的知識主要針對上一節(jié)中敘述的問題展開,針對上述三個方面的問題,應(yīng)該從以下兩個方面,七個具體問題入手。
3.1程序設(shè)計理念上的轉(zhuǎn)變
程序設(shè)計理念上的轉(zhuǎn)變主要是從了解面向?qū)ο蠹夹g(shù)產(chǎn)生的背景出發(fā),理解面向?qū)ο蠹夹g(shù)產(chǎn)生的自然性及其相對于過程化程序設(shè)計的優(yōu)勢,從程序語言發(fā)展和設(shè)計兩個角度上理解,從而能對面向?qū)ο蠹夹g(shù)有一個全局性的把握。
1.從程序語言發(fā)展的角度出發(fā)看面向?qū)ο蟪绦蛟O(shè)計語言出現(xiàn)的必要性及其與過程語言的銜接性。程序設(shè)計的發(fā)展的目標(biāo)之一是提高重用性,而提高重用性的方法是提高抽象性,C語言提供的抽象機制是函數(shù),這是過程化程序設(shè)計語言的特點。這樣的抽象能力是很有限的,在大型系統(tǒng)的設(shè)計中會造成結(jié)構(gòu)復(fù)雜、管理困難的現(xiàn)象,同時代碼難以理解、難以維護、難以重用。面向?qū)ο笳Z言在這方面顯示了強大的優(yōu)勢。面向?qū)ο蠹夹g(shù)提供的抽象機制主要是類(Class),也叫做用戶自定義類型,類是一種完全的用戶自定義類型。面向?qū)ο蟪绦蛟O(shè)計技術(shù)可以在很大程度上解決過程化程序設(shè)計所面臨的問題。首先,面向?qū)ο蟮脑O(shè)計可以很好地模擬現(xiàn)實世界,大大縮小了現(xiàn)實問題域與程序設(shè)計問題域的差距,使得客戶也可以理解設(shè)計,對設(shè)計的修改和驗證也變得很容易。其次,類封裝了內(nèi)部屬性,只對外部暴露接口,只要不更改接口,在類的內(nèi)部所做的任何修改不會對類的使用者造成影響,大大提高了可重用性和可維護性。
2.從設(shè)計的角度出發(fā)觀察面向?qū)ο蟮脑O(shè)計與過程化設(shè)計上的區(qū)別。從設(shè)計的角度出發(fā)學(xué)習(xí)程序設(shè)計很有好處,通過對設(shè)計的思考,非常有助于了解某種程序設(shè)計范型的優(yōu)勢和缺點,同樣,通過對面向?qū)ο笤O(shè)計的思考,有利于更好的掌握面向?qū)ο蟮募夹g(shù)內(nèi)涵。舉一個例子,設(shè)計圖書館管理系統(tǒng)。如果是過程化設(shè)計,關(guān)注的是圖書館有哪些功能,每一個功能該怎樣實現(xiàn),需要哪些函數(shù),這些函數(shù)之間應(yīng)該怎樣相互調(diào)用以完成該功能。大概可以這樣設(shè)計:分為圖書借閱、圖書整理、人員管理三個部分,如圖1所示。每個部分又劃分為若干子功能,每一個功能都可能成為以后的一個函數(shù)。每一個函數(shù)可能又調(diào)用更小的函數(shù),通過逐級的函數(shù)調(diào)用完成功能。在面向?qū)ο蟮姆治鲋幸矂澐止δ軌K,但只是為了確定需求,設(shè)計上與過程化設(shè)計大大不同:參與圖書館系統(tǒng)所有的實體都對應(yīng)設(shè)計中的一個類,系統(tǒng)功能的實現(xiàn)通過類之間的交互完成,設(shè)計的問題就變成了類以及類之間關(guān)系的設(shè)計,如圖2所示。通過兩者對比可以看出,面向?qū)ο笤O(shè)計更貼近真實、更容易理解。也可以看出,兩者在理念上有多么大的區(qū)別。
3.2掌握C++實現(xiàn)面向?qū)ο髾C制所必須的知識
有了編程理念上的轉(zhuǎn)變,理解了面向?qū)ο蠹夹g(shù)產(chǎn)生的技術(shù)需求和面向?qū)ο蠓缎偷奶匦?,還必須具備掌握C++實現(xiàn)面向?qū)ο髾C制所必須的知識才能很好的理解面向?qū)ο蠹夹g(shù),這些預(yù)備知識是C語言中所缺乏的,可以說,對這些預(yù)備知識的良好掌握是理解面向?qū)ο蠹夹g(shù)的關(guān)鍵,總結(jié)一下有五個方面的內(nèi)容:
1.引用
引用在面向?qū)ο蟪绦蛟O(shè)計中十分常用,而且不同的程序設(shè)計語言在引用的使用上又不盡相同,比如C++和java的引用就有很大的不同。透徹的理解引用非常重要,但對新手來說很不容易,可以說正確的理解引用機制是學(xué)習(xí)好面向?qū)ο蠹夹g(shù)的第一步。C語言中沒有引用的概念,需要引用的地方只能用指針去實現(xiàn)。在C++中引用的使用十分普遍,也比較復(fù)雜,而且更多的是用在對自定義類型的操作上。對引用的學(xué)習(xí)應(yīng)該從使用引用的場合入手,有三個方面的內(nèi)容,每個方面應(yīng)分兩個部分講解:格式(怎樣做)和意義(怎樣用)。
●引用型變量。定義一個整型引用型變量yi的格式:
intyi = i;// 變量i是已經(jīng)被定義過的整型變量
意義:yi是i的別名,兩者指向同一變量,是同一個變量的兩個名字,無論修改誰,變量都將發(fā)生修改。
注意事項:引用型變量在定義的時候一定要初始化,不能先定義,再初始化,這一點與java不同。
●引用型參數(shù)(傳引用,傳址)。定義引用型參數(shù)的格式如下:
返回類型函數(shù)名(參數(shù)類型參數(shù)名){ 函數(shù)體 },與非引用參數(shù)的不同之處在于參數(shù)名前多了一個引用符號“”。
意義:C++默認傳值,也就是說在函數(shù)體內(nèi)對形參做的任何修改不會影響到實參,在函數(shù)運行之前先調(diào)用實參的拷貝構(gòu)造函數(shù)生成一個臨時變量,這個臨時變量在函數(shù)體內(nèi)部代替形參參與運算,這就是為什么不用引用或者指針就無法通過函數(shù)實現(xiàn)交換兩個變量的值,也就是C++中調(diào)用拷貝構(gòu)造函數(shù)的時機之一,如果出于效率或者想修改實參,就必須用引用型參數(shù),這樣進入函數(shù)體內(nèi)部參與運算的就是實參本身。
●引用型返回值。定義引用型返回值的格式如下:
返回類型函數(shù)名(參數(shù)類型參數(shù)名){ 函數(shù)體 }
意義:函數(shù)體內(nèi)的任何臨時變量在函數(shù)運行完畢后都要銷毀,如果要返回某個變量,那么將會調(diào)用這個變量所屬類型的拷貝構(gòu)造函數(shù)構(gòu)造一個臨時變量返回,除非使用引用型返回值。假設(shè)有如下程序,其中類A有一個函數(shù)fun意圖返回對象自己,我們看它這樣做能辦到嗎?
運行,打印以下結(jié)果:
構(gòu)造函數(shù)
拷貝構(gòu)造
拷貝構(gòu)造
程序運行結(jié)果顯示調(diào)用了兩次拷貝構(gòu)造函數(shù),為什么會這樣呢?兩次拷貝構(gòu)造函數(shù)調(diào)用時機為:一次是執(zhí)行“return *this; ”,編譯器會在以*this為實參構(gòu)造臨時變量時調(diào)用拷貝構(gòu)造函數(shù),另一次是執(zhí)行“A a1 = a.fun ();”,編譯器會在以上一步構(gòu)造的臨時變量為實參初始化a1時調(diào)用拷貝構(gòu)造函數(shù)。現(xiàn)在把“A fun (){ return *this; }” 改為“A fun (){ return *this; }”,再運行可以得到如下的結(jié)果:
構(gòu)造函數(shù)
拷貝構(gòu)造
可以看出只在初始化a1時運行了一次拷貝構(gòu)造函數(shù),返回的正是對象自己。
注意事項:引用型返回值一般用在返回引用型參數(shù)(如C++中重載的輸出運算符“<<”),或者返回對象自己(如重載的賦值運算符),不能返回函數(shù)體內(nèi)臨時變量的引用,因為函數(shù)運行完畢后臨時變量全部都要銷毀,對象已不存在了,無法返回對象本身[1]。
總結(jié):C++中引用的使用比較復(fù)雜,原因在于兩個方面:首先在C++中默認是傳值的,而不是傳址(傳引用),其次在C++中用不同的方法造出來的變量的存儲位置不同、生命周期不同、銷毀方法和銷毀責(zé)任也不相同。所以在C++中使用引用要十分小心,需要對引用的透徹理解,而且在某些方面必須使用引用,比如拷貝構(gòu)造函數(shù)。所以可以說學(xué)好引用是掌握C++實現(xiàn)面向?qū)ο髾C制的重要的語言基礎(chǔ)。
2.Const關(guān)鍵字
在C語言中沒有常量的表示法,如果要定義一個常量只能用#define,這已經(jīng)被證明非常不好,因為在編譯時將用變量值代替變量名,不利于調(diào)試。所以在C++中鼓勵以常量表示法替代#define。定義一個常量的格式如下:
const 類型 變量名 = 初值;
定義常量時一定要同時賦初值,因為常量一旦被賦值就不能修改,如果定義時不賦值,那么常量將會以默認值初始化,這一般都不是我們想要的。Const不但能修飾一般變量使之成為常量,也可以修飾引用和指針,在這里我們著重討論const與引用結(jié)合的情況:在引用使用的三種場合怎樣使用const以及跟C++實現(xiàn)面向?qū)ο蠹夹g(shù)的關(guān)系。
●定義常引用變量
定義一個常引用變量的格式如下:
const類型 變量名 = 某個已定義的變量名
意義:不能改變常引用使之指向另一個對象,也不能通過常引用去改變該引用所指向的對象的值。
●常引用型參數(shù)
返回類型函數(shù)名(const參數(shù)類型參數(shù)名){ 函數(shù)體 },跟引用參數(shù)的不同之處在于參數(shù)類型前多了一個常量符號“const”。
意義:當(dāng)需要明確的表示不希望函數(shù)對實參修改,但是為了效率或者其他的原因又需要引用型參數(shù)時,可以使用常引用型參數(shù)。使用常引用型參數(shù)的一個常見場合是拷貝構(gòu)造函數(shù)和賦值運算符的重載??截悩?gòu)造函數(shù)必須使用引用型參數(shù),原因在于如果傳值,那么在實參傳入之前會調(diào)用編譯器提供的默認的拷貝構(gòu)造函數(shù)來構(gòu)造一個實參的副本,這樣已經(jīng)失去了自己寫拷貝構(gòu)造函數(shù)的意義。
●常引用型返回值
const返回類型函數(shù)名(參數(shù)類型參數(shù)名){ 函數(shù)體 }
意義:當(dāng)使用引用型返回值時,可以直接對返回值進行修改,當(dāng)不想修改函數(shù)的返回值時,可以使用常引用型返回值。
3.函數(shù)重載
C語言不提供函數(shù)重載,完成相似功能的函數(shù)必須使用不同的名稱。這樣造成在大型程序開發(fā)中,給函數(shù)命名很難。C++解決了這個問題,當(dāng)然C++必須要解決這個問題,原因在于構(gòu)造函數(shù)名必須要與類名相同,而構(gòu)造函數(shù)又不能將參數(shù)固定,就是要允許用不同的參數(shù)列表去初始化對象,C++提供了函數(shù)重載。所以學(xué)好函數(shù)重載是掌握構(gòu)造函數(shù)的一個重要基礎(chǔ)。函數(shù)重載的要求:1)函數(shù)名相同;2)參數(shù)列表不同。參數(shù)類型,個數(shù)或順序(不提倡僅僅是順序)不同。3)不能使用返回值重載[2],原因在于調(diào)用函數(shù)時可以僅僅利用函數(shù)的副作用,也就是不取得函數(shù)的返回值。
4.new與delete
在C語言中分配和回收內(nèi)存是用malloc和free,轉(zhuǎn)到C++上之后,應(yīng)該完全使用new與delete,原因在于malloc和free對構(gòu)造函數(shù)和析構(gòu)函數(shù)一無所知[1],使用new與delete構(gòu)造和刪除對象會自動調(diào)用該對象所屬類型的構(gòu)造函數(shù)和析構(gòu)函數(shù),也會調(diào)用該類型內(nèi)所有自定義類型的成員對象的構(gòu)造函數(shù)和析構(gòu)函數(shù),而這些是malloc和free無法做到的。也就是new和delete可以做malloc和free所做的所有事情,而且做的更好,還可以適用于自定義類型的構(gòu)造和析構(gòu),所以在學(xué)習(xí)了C++以后所有分配和銷毀內(nèi)存的事情應(yīng)該交給new與delete去做。
5.cout與cin
C語言的輸入輸出是調(diào)用printf和scanf函數(shù),它不能判斷輸入輸出的對象類型,必須進行參數(shù)設(shè)置,不能實現(xiàn)對用戶自定義類型的輸入和輸出,而且格式復(fù)雜。cout與cin是C++的標(biāo)準(zhǔn)輸入輸出流對象,它們可以用在任何重載了“<<”和“>>”運算符的對象上以實現(xiàn)輸入和輸出,不管基本類型還是自定義類型都可以直接輸入輸出,而且有多種格式化函數(shù)可以調(diào)用,所以在學(xué)習(xí)了C++后,所有的輸入輸出都應(yīng)用使用cout和cin,不過,這一部分內(nèi)容可以放到運算符重載的章節(jié)重點講解。
4結(jié)論
俗話說“工欲善其事,必先利其器”,在理解面向?qū)ο蟪绦蛟O(shè)計技術(shù)的內(nèi)涵,學(xué)習(xí)C++語言實現(xiàn)面向?qū)ο髾C制之前,掌握好這些基礎(chǔ)知識是很重要的,它們就相當(dāng)于“器”。重要的概念,比如說引用、Const關(guān)鍵字和函數(shù)重載可以先講,其余的內(nèi)容可以放到它們出現(xiàn)的章節(jié)中進行重點講解,但是這些內(nèi)容不容忽視,否則會給理解面向?qū)ο蠹夹g(shù)的內(nèi)涵造成很大的障礙。
The Discussion about preliminary knowledge in teaching object oriented programming course in C++
Abstract: Many universities use C++ to teach the course: object oriented programming. The paper discusses two preliminary works that should do before study the object oriented technology: 1. Programming thought changing from procedural paradigm to object oriented paradigm; 2. The preliminary technologies that the C++ programming language used to realize object oriented technology. The paper gives elaborate discussion for the two aspects above too.
Keywords: procedural; object oriented; paradigm; class
參考文獻
[1] Scott Meyers. Effective C++中文版(第二版)[M]. 侯捷 譯. 武漢:華中科技大學(xué)出版社,2001.
[2] Bjarne Stroustrup. C++程序設(shè)計語言(特別版)[M]. 裘宗燕 譯. 北京:機械工業(yè)出版社,2002.
[3] 馬艷. 提高”C++語言程序設(shè)計”課程教學(xué)效果之我見[J]. 新疆廣播電視大學(xué)學(xué)報,2005,9(29):49-51.
[4] 陳真,王釗. C++面向?qū)ο蟪绦蛟O(shè)計中的構(gòu)造函數(shù)[J]. 電氣電子教學(xué)學(xué)報,2006,28(6):83-85.
[5] 劉曉剛. 用多種手段教學(xué)C++課程[J]. 湖北廣播電視大學(xué)學(xué)報,2003,21(3):48-50.
作者簡介
戴建國,石河子大學(xué)信息科學(xué)與技術(shù)學(xué)院教師,專業(yè)方向為軟件工程。
郭理,石河子大學(xué)信息科學(xué)與技術(shù)學(xué)院教師,專業(yè)方向為人工智能。
秦懷斌,石河子大學(xué)信息科學(xué)與技術(shù)學(xué)院教師,專業(yè)方向為軟件工程。