C++語(yǔ)言僅僅是面對(duì)對(duì)象的語(yǔ)言嗎
C++語(yǔ)言實(shí)際上是幾種不同語(yǔ)言的聚集地,你可以把他看成一種語(yǔ)言,也可以把他看成一種語(yǔ)言,要進(jìn)行對(duì)C++程序地開(kāi)發(fā),那么復(fù)雜性會(huì)大幅度地增加,這就是C++在實(shí)踐中難于控制的一個(gè)主要原因。
混合使用不同風(fēng)格,就好像在一個(gè)源文件里混合使用多種不同的語(yǔ)言,復(fù)雜和不一致性必然暴露。當(dāng)然,C++獨(dú)特的魅力正在于混合風(fēng)格編程的強(qiáng)大威力。這正是一把雙刃劍,雖然具有潛在的強(qiáng)大威力,但是通常來(lái)說(shuō)也是導(dǎo)致項(xiàng)目混亂的重要原因。
我認(rèn)為以下面的原則進(jìn)行實(shí)際開(kāi)發(fā),將可以在一定程度上規(guī)避風(fēng)險(xiǎn):
1). 在任何一個(gè)單個(gè)的時(shí)間點(diǎn),只使用一種編程風(fēng)格。
2). 以一種風(fēng)格為主風(fēng)格,用它來(lái)組織整體模塊的開(kāi)發(fā)。
3). 在遇到特別適合另一種風(fēng)格的典型場(chǎng)景,可以用一個(gè)子模塊包裝該場(chǎng)景,然后在該子模塊中使用該風(fēng)格,但記住遵循要求1,避免混合風(fēng)格。此外,必須通過(guò)封裝手段將該模塊包裝起來(lái),以符合主體風(fēng)格的要求。比如說(shuō),主風(fēng)格是better C,在某個(gè)子模塊中用到了面向?qū)ο?,則應(yīng)當(dāng)使這個(gè)子模塊從整體上看來(lái)像是一個(gè)普通的C過(guò)程。
4). 在個(gè)別場(chǎng)合,混合風(fēng)格的確有很大的好處。但是,這種情形是比較少見(jiàn)的,一般來(lái)說(shuō)比較成功的實(shí)踐已經(jīng)總結(jié)成patterns,所以在工程實(shí)際中,可以強(qiáng)行規(guī)定,只有在符合某個(gè)patterns的情況下才可以謹(jǐn)慎地使用混合風(fēng)格,嚴(yán)禁擅自創(chuàng)造新的混合用法。
現(xiàn)在來(lái)討論一下究竟應(yīng)當(dāng)如何劃分C++風(fēng)格。Stroustrup對(duì)C++風(fēng)格的分類是從語(yǔ)言開(kāi)發(fā)者的角度進(jìn)行的。如果我們以下面的原則進(jìn)行分類,我認(rèn)為會(huì)得出不同的結(jié)果:
1) 每一種風(fēng)格必須構(gòu)成一個(gè)完整的子語(yǔ)言,具有完備性,可以單獨(dú)使用這一子語(yǔ)言開(kāi)發(fā)任何軟件系統(tǒng),有經(jīng)過(guò)歷史驗(yàn)證的成功經(jīng)驗(yàn)。
2) 每一種風(fēng)格必須相對(duì)簡(jiǎn)單,有一致的、簡(jiǎn)單的、得到認(rèn)可和驗(yàn)證的原則。
3) 每一種子語(yǔ)言必須能夠在現(xiàn)實(shí)世界中找到相對(duì)應(yīng)的其他語(yǔ)言。
依據(jù)以上原則,我將C++語(yǔ)言劃分為三個(gè)半子語(yǔ)言:
1) Better C, 只增加函數(shù)重載、引用類型、缺省參數(shù)等簡(jiǎn)單特性的類C子集。對(duì)應(yīng)ANSI C語(yǔ)言。
2) ADT C++,即C with Class,整個(gè)程序由平面化的具體類(concrete class)對(duì)象構(gòu)成,無(wú)繼承,無(wú)多態(tài)。對(duì)應(yīng)Ada 83語(yǔ)言。
3) IDL C++,我稱之為Interface-Oriented,典型范例是COM組件模型。
3.5) GP C++, 利用模板技術(shù)形成了一種庫(kù)和組件的實(shí)現(xiàn)語(yǔ)言。這不是一種完整子語(yǔ)言,一方面因?yàn)榭梢园阉闯墒茿DT C++的一種延伸,另一方面它必須依附于其他風(fēng)格而發(fā)揮作用。
顯然,我這里遺留了一個(gè)最重要的風(fēng)格,也就是我們通常所說(shuō)的“傳統(tǒng)面向?qū)ο蟆憋L(fēng)格,由Smalltalk,Java等語(yǔ)言所展示的。由MFC等類庫(kù)經(jīng)過(guò)多年實(shí)踐論證了的一種風(fēng)格:靠龐大的繼承樹(shù)抽象和組織各種數(shù)據(jù)類型,靠繼承和組合實(shí)現(xiàn)代碼復(fù)用。這種風(fēng)格為什么沒(méi)有被我提及呢?
因?yàn)槲艺J(rèn)為這種風(fēng)格實(shí)際上是一種混合風(fēng)格!可以認(rèn)為是在試圖融合上述第2、3和3.5種風(fēng)格。在前述的三條原則里,它嚴(yán)重地違背了第二條。由于C++的靜態(tài)本質(zhì),由于C++缺乏天然的類庫(kù)和垃圾收集機(jī)制,使得在C++語(yǔ)言中進(jìn)行Smalltalk風(fēng)格的編程非常非常困難,以至于為了克服這些困難,C++語(yǔ)言實(shí)際上發(fā)展出了一套不同于Smalltalk、Java風(fēng)格的獨(dú)特的“面向?qū)ο蟆本幊田L(fēng)格。
這套風(fēng)格歷經(jīng)近15年實(shí)踐,應(yīng)該說(shuō)有成功有失敗,雖然出版了大量的著作,至今沒(méi)有形成簡(jiǎn)單的、一致的、可仿效的風(fēng)格指導(dǎo)。從某種意義上說(shuō),如此多的C++面向?qū)ο缶幊讨笇?dǎo)書(shū)籍十幾年常盛不衰,恰恰說(shuō)明這種風(fēng)格的困難程度和難以仿效性。
就我個(gè)人而言,我已經(jīng)不再以這種風(fēng)格為指導(dǎo)思想了。我不會(huì)再拼命地構(gòu)造繼承樹(shù),思考哪些函數(shù)應(yīng)該是虛函數(shù)這類問(wèn)題了。你可以認(rèn)為“為了復(fù)用代碼而進(jìn)行的繼承”是這種風(fēng)格的標(biāo)志。
請(qǐng)注意,ADT C++允許組合,對(duì)于繼承則應(yīng)該想盡一切辦法避免。而IDL C++的典型代表COM,根本就不支持這種繼承,它支持的只是接口的復(fù)用。當(dāng)然,這并不是要否定十幾年來(lái)C++語(yǔ)言在面向?qū)ο蠓矫姘l(fā)展的成績(jī)。但是,如果你現(xiàn)在從頭開(kāi)始規(guī)劃一個(gè)完整的項(xiàng)目,那么我認(rèn)為如果選擇這種雜合風(fēng)格,是不太明智的。但是這種風(fēng)格也有兩個(gè)典型的使用場(chǎng)景:
1) 有一個(gè)完整的框架支持。比如MFC。雖然這種風(fēng)格本身有很多技術(shù)難點(diǎn),但是MFC這樣的框架已經(jīng)幫你克服了一部分,給你營(yíng)造了一個(gè)類似Smalltalk那樣的、相對(duì)舒適環(huán)境,這時(shí)候可以使用這種風(fēng)格。但是通常要認(rèn)識(shí)到,這類框架在克服不少技術(shù)難點(diǎn)的同時(shí),引入了一些新的問(wèn)題,有時(shí)是更加難以對(duì)付的問(wèn)題,所以要明智,并且做好充分準(zhǔn)備。
2) 符合經(jīng)典模式。如果遇到某個(gè)典型的“面向?qū)ο蟆眻?chǎng)景,已經(jīng)有了成熟的、優(yōu)秀的、現(xiàn)成的、文檔化了的設(shè)計(jì)解決方案,則可以有選擇的、謹(jǐn)慎地使用之。我指的主要就是GoF和其他一些設(shè)計(jì)模
這里所謂的“經(jīng)典模式”數(shù)量絕對(duì)不會(huì)太多,但是卻大量地、反復(fù)地出現(xiàn)在設(shè)計(jì)中,并且往往復(fù)合出現(xiàn)。這樣的情況用已經(jīng)經(jīng)過(guò)驗(yàn)證的設(shè)計(jì)方案來(lái)解決是非常合適我個(gè)人在這里有一些實(shí)踐,覺(jué)得應(yīng)該注意幾個(gè)問(wèn)題。***是要謹(jǐn)慎,我遇到過(guò)大量的情形,看上去很適合用某個(gè)模式來(lái)解決,但是真的用了才發(fā)現(xiàn)并不是這么回
在不適合的地方套用了錯(cuò)誤的模式,會(huì)把事情弄得一團(tuán)糟;二是***將設(shè)計(jì)方案局部化,包裝起來(lái),從外面看不出你使用了什么模式。三是注意內(nèi)存問(wèn)題。使用OO風(fēng)格的***障礙其實(shí)就是內(nèi)存問(wèn)題。
【編輯推薦】