基本介紹
OOP: Object Oriented Programming,面向對象的程式設計。所謂“對象”在顯式支持面向對象的語言中,一般是指類在記憶體中裝載的實例,具有相關的成員變數和成員函式(也稱為:方法)。
面向對象的程式設計完全不同於傳統的
面向過程程式設計,它大大地降低了軟體開發的難度,使編程就像搭積木一樣簡單,是當今
電腦編程的一股勢不可擋的潮流。
OOP 達到了軟體工程的三個主要目標:重用性、靈活性和擴展性。為了實現整體運算,每個對象都能夠接收信息、處理數據和向其它對象傳送信息。OOP 主要有以下的概念和組件:
組件 - 數據和功能一起在運行著的電腦程式中形成的
單元,組件在 OOP 電腦程式中是模組和結構化的基礎。
抽象性 - 程式有能力忽略正在處理中信息的某些方面,即對信息主要方面關注的能力。
封裝 - 也叫做信息封裝:確保組件不會以不可預期的方式改變其它組件的內部狀態;只有在那些提供了內部狀態改變方法的組件中,才可以訪問其內部狀態。每類組件都提供了一個與其它組件聯繫的接口,並規定了其它組件進行調用的方法。
多態性 - 組件的引用和類集會涉及到其它許多不同類型的組件,而且引用組件所產生的結果依據實際調用的類型。
繼承性 - 允許在現存的組件基礎上創建子類組件,這統一併增強了多態性和封裝性。典型地來說就是用類來對組件進行分組,而且還可以定義新類為現存的類的擴展,這樣就可以將類組織成樹形或網狀結構,這體現了動作的通用性。
由於抽象性、封裝性、重用性以及便於使用等方面的原因,以組件為基礎的編程在
腳本語言中已經變得特別流行。Python 和 Ruby 是最近才出現的語言,在開發時完全採用了 OOP 的思想,而流行的 Perl 腳本語言從版本5開始也慢慢地加入了新的
面向對象的功能組件。用組件代替“現實”上的實體成為 JavaScript(ECMAScript) 得以流行的原因,有論證表明對組件進行適當的組合就可以在英特網上代替 HTML 和 XML 的
文檔對象模型(DOM)。
OOP思想
面向對象編程技術的關鍵性觀念是它將數據及對數據的操作行為放在一起,作為一個相互依存、不可分割的整體——對象。對於相同類型的對象進行分類、抽象後,得出共同的特徵而形成了類。面向對象編程就是定義這些類。類是描述相同類型的對象集合。類定義好之後將作為數據類型用於創建類的對象。程式的執行表現為一組對象之間的互動通信。對象之間通過公共接口進行通信,從而完成系統功能。類中聲明的
public成員組成了對象的對外公共接口。簡單來說就是以功能為解決問題的中心。
基本思想
OOP的許多原始思想都來之於Simula語言,並在Smalltalk語言的完善和標準化過程中得到更多的擴展和對以前的思想的重新註解。可以說OO思想和OOPL幾乎是同步發展相互促進的。與
函式式程式設計(functional-programming)和邏輯式程式設計(logic-programming)所代表的接近於機器的實際計算模型所不同的是,OOP幾乎沒有引入精確的數學描敘,而是傾向於建立一個對象模型,它能夠近似的反映套用領域內的實體之間的關係,其本質是更接近於一種人類認知事物所採用的哲學觀的計算模型。由此,導致了一個自然的話題,那就是OOP到底是什麼?[D&T 1988][B.S 1991] .。在OOP中,對象作為計算主體,擁有自己的名稱,狀態以及接受外界訊息的接口。在對象模型中,產生新對象,舊對象銷毀,傳送訊息,回響訊息就構成OOP計算模型的根本。
對象的產生有兩種基本方式。一種是以原型(prototype)對象為基礎產生新的對象。一種是以類(class)為基礎產生新對象。原型的概念已經在認知心理學中被用來解釋概念學習的遞增特性,
原型模型本身就是企圖通過提供一個有代表性的對象為基礎來產生各種新的對象,並由此繼續產生更符合實際套用的對象。而原型-委託也是OOP中的對象抽象,
代碼共享機制中的一種。一個類提供了一個或者多個對象的通用性描述。從形式化的觀點看,類與類型有關,因此一個類相當於是從該類中產生的實例的集合。而這樣的觀點也會帶來一些矛盾,比較典型的就是在繼承體系下,子集(子類)對象和父集(父類)對象之間的行為相融性可能很難達到,這也就是OOP中常被引用的---子類型(subtype)不等於子類(subclass)[Budd 2002]。而在一種所有皆對象的世界觀背景下,在類模型基礎上還誕生出了一種擁有元類(metaclass)的新對象模型。即類本身也是一種其他類的對象。以上三種根本不同的觀點各自定義了三種基於類(class-based),基於原型(prototype-based)和基於元類(metaclass-based)的對象模型。而這三種對象模型也就導致了許多不同的
程式設計語言(如果我們暫時把靜態與動態的差別放在一邊)。是的,我們經常接觸的C++,Java都是使用基於類的對象模型,但除此之外還有很多我們所沒有接觸的OOPL採用了完全不一樣的對象模型,他們是在用另外一種觀點詮釋OOP的內涵。
什麼是oop的基本思想呢?把組件的實現和接口分開,並且讓組件具有
多態性。不過,兩者還是有根本的不同。oop強調在程式構造中語言要素的語法。你必須得繼承,使用類,使用對象,對象傳遞訊息。不關心你繼承或是不繼承,它的開端是分析產品的分類,有些什麼種類,他們的行為如何。就是說,兩件東西相等意味著什麼?怎樣正確地定義相等操作?不單單是相等操作那么簡單,你往深處分析就會發現“相等”這個一般觀念意味著兩個對象部分,或者至少基本部分是相等的,據此我們就可以有一個通用的相等操作。再說對象的種類。假設存在一個順序序列和一組對於順序序列的操作。那么這些操作的語義是什麼?從複雜度權衡的角度看,我們應該向用戶提供什麼樣的順序序列?該種序列上存在那些操作?那種排序是我們需要的?只有對這些組件的概念型分類搞清楚了,我們才能提到實現的問題:使用模板、繼承還是宏?使用什麼語言和技術?gp的基本觀點是把抽象的
軟體組件和它們的行為用標準的分類學分類,出發點就是要建造真實的、高效的和不取決於語言的算法和數據結構。當然最終的載體還是語言,沒有語言沒法編程。stl使用c++,你也可以用ada來實現,用其他的語言來實現也行,結果會有所不同,但基本的東西是一樣的。到處都要用到
二分查找和排序,而這就是人們正在做的。對於容器的語義,不同的語言會帶來輕微的不同。但是基本的區別很清楚是gp所依存的語義,以及語義分解。例如,我們決定需要一個組件swap,然後指出這個組件在不同的語言中如果工作。顯然重點是語義以及語義分類。而oop所強調的(我認為是過分強調的)是清楚的定義類之間的層次關係。oop告訴了你如何建立層次關係,卻沒有告訴你這些關係的實質。
(這段不太好理解,有一些術語可能要過一段時間才會有合適的中文翻譯——譯者)
面向對象的編程方法OOP是九十年代才流行的一種軟體編程方法。它強調對象的“抽象”、“封裝”、“繼承”、“
多態”。我們講程式設計是由“數據結構”+“算法”組成的。從巨觀的角度講,OOP下的對象是以編程為中心的,是面向程式的對象。
特徵
1.1面向對象程式設計的特徵:
1) 封裝
1.2類與數據封裝
1.2.1什麼是類?
簡單的說,類就是一種用戶定義的數據類型,跟結構類似;並且,類具有自己的
成員變數和成員函式(方法),通過它們可以對類自身進行操作。如:汽車可以看作是發動機、車輪、座椅等諸如此類的集合。也可以從功能的角度來研究,譬如,能移動,加速,減速,剎車等。
例如:
class CMyClass1
{
protected:
CMyClass1();
public:
virtual ~ CMyClass1();
}
1.2.2 封裝(encapsulation)
定義:指能夠把一個實體的信息、功能、回響都裝入一個單獨的對象中的特性。封裝的優點如下:
1) 封裝允許類的客戶不必關心類的工作機理就可以使用它。就象駕駛員不必了解發動機的工作原理就可以駕駛汽車一樣,類的客戶在使用一個類時也不必了解它是如何工作的,而只需了解它的功能即可。
2) 所有對數據的訪問和操作都必須通過特定的方法,否則便無法使用,從而達到
數據隱藏的目的。
1.2.3 對象
對象就是類的實例。類與對象的關係就如類型和變數的關係,所有對類的操作都必須通過對象來實現。當一個類定義了多個對象時,每個對象擁有各自的成員數據。
1.2.4 類的三種成員類型
1) 私有成員(private):預設情況下,一個類中的所有成員都是私有的。私有成員只能被類本身的成員函式訪問。能夠被繼承但是被繼承的私有成員不能夠使用。
2) 公有成員(public):公有成員可以被類成員函式和外部函式使用。
3) 保護成員(protected):類的保護成員能被類及其派生
類的成員函式和友元函式使用,具有繼承性。
1)構造函式
a. 是特殊的成員函式;在創建對象時首先由系統自動調用。它的作用是為新創建的對象分配空間,或為該對象的
成員變數賦值等;
b. 構造函式名必須與其類名稱完全相同,並且不允許有返回值。
2)析構函式
a. 析構函式是構造函式的逆操作;
b. 析構函式在類名之前加~來命名,它不允許有返回值,也不允許帶
參數,並且一個類只能有一個析構函式。
1.3繼承
1.3.1 傳統程式設計的缺點:
繼承的方法允許在不改動原程式的基礎上對其進行擴充,這樣使得原功能得以保存,而新功能也得以擴展。這有利於減少重複編碼,提高軟體的開發效率。
1)一個類可以繼承其它類的成員,被繼承的類叫基類或
父類;繼承類叫
派生類或
子類2)派生類不但擁有自己的成員變數和成員函式,還擁有父類的成員變數和成員函式。
1.3.4 類的保護成員(protected)
前面介紹了類的私有成員只能被類的成員函式和友員函式使用;類的保護成員能被類及其派生類的成員函式和友員函式使用。也就是說,類的保護成員具有繼承性,而類的私有成員不具有繼承性。
1)公用基類中的所有public成員在
派生類中仍是public成員,所有protected成員在派生類中仍是protected成員。
2)私有基類中的public成員和protected成員在派生類中均變成private成員。
1)多重繼承的定義方法
例子:
class A
{
…
public:
int i;
void func1();
…
};
class B
{
…
public:
int i;
void func1();
…
};
class C: public A,B
{
…
void Show()
…
};
預設情況下
基類被定義為 private;因此基類B為私有基類。
2)繼承的不確定性
例子:
class C:public A,B
{
…
void Show()
{
j = i*i;
func1();
}
…
};
由於
基類A和B中同時擁有
數據成員i和成員函式func1,類C引用基類的成員時,系統無法分辨是調用哪一基類的成員而發生錯誤;
使用
域操作符指明要調用的基類,即可解決不確定性問題。
class C:public A,B
{
…
int j;
void Show()
{
j = A::i*B::i;
A::func1();
}
…
};
1.3.7 多層繼承
定義:所謂多層繼承指的是從一個類派生出另一個類,然後以
派生類作為
基類,派生出另一個類,直到最後生成的派生類滿足需要為止(見MSDN中的
Hierarchy Chart)。
在繼承關係下,派生類的構造函式負責調用基類的構造函式來設定基類
數據成員值。
例:
class base
{//基類
…
public:
int i;
base(int j)
{//構造函式
i = j;
}
…
};
class derived:public base
…
public:
double f;
derived(int, double);
…
};
derived::derived(int k, double l):base(k)
…
f = l;
…
}
1) 在定義派生類對象時,系統首先調用
基類的構造函式,然後調用派生類的構造函式;在上例中,derived類首先調用base類的構造函式,然後調用自身的構造函式。
2)
析構函式的調用順序與構造函式的調用順序相反。
1.4重載
1)如果函式有相同的名稱和返回值,而有不同的參數個數或參數類型,則這些函式就是
重載函式。
2)派生類繼承了
基類的某一函式,並且又自定義了一個同名函式,有相同的返回值,不同的參數類型或參數個數。這種情況不屬於
重載。因為它們屬於不同的域。
3)例:
class base
{
…
void func(int i)
{
…
}
void func(double f)
{
…
}
void func(double f, long q)
{
…
}
…
};
重載操作符的定義:返回值類型 operator op (參數表);其中,op為重載操作符,它必須是VC++中所定義的
運算符。然後像定義函式一樣定義重載操作符函式。
例子:
class person
{
…
int age;
void operator ++();
…
};
void person::operator++()
{
age++;
}
1.5虛擬函式與多態性
1)函式的定義:在定義類時在其成員函式前加上關鍵字virtual;
2)如果
基類中成員函式定義為
虛函式,則派生類中與其定義完全相同的成員函式,
編譯器自動將其視為虛函式;
3)只有類的成員函式才能定義為虛函式。
4)虛擬成員函式的存取要看首次定義它的類中,該函式是public還是private。
例:
class Insect
{
…
virtual bool CanFly();
…
};
bool Insect :: CanFly()
{
return FALSE;
}
class Butterfly:public Insect
{
…
bool CanFly();
…
};
bool Butterfly :: CanFly()
{
return TRUE;
}
2) 可以使用
基類對象調用派生類對象,即將派生類對象或
指針賦值給基類對象或指針。
3) 反方向的賦值(將基類的對象或指針賦給派生類的對象或指針)是危險的。
例:
bool rtn;
Insect inc1,*pInc;
Butterfly btfly;
pInc = &inc1; //pInc指針指向Insect對象
rtn = pInc->CanFly(); //返回FALSE
pInc = &btfly; //pInc指針指向Butterfly對象
rtn = pInc->CanFly(); //返回TRUE
1)形式上,重載函式要求有相同的返回值類型和函式名,並有不同的參數序列;而虛擬函式要求三者完全相同。
2)重載函式可以是成員函式或非成員函式;而虛擬函式必須是成員函式。
3)調用方法上,重載函式根據所傳遞的參數序列的差別作為調用的依據;而虛擬函式則根據調用對象的不同而去調用不同類的函式。
4)虛擬函式在運行時表現出
多態功能;而重載函式不具有這一功能。
定義:virtual type funcname(parameter)=0;
C++中有時設計
基類就是為了被繼承,而基類中的
虛擬函式不做任何工作,這種情況下可以將基類中的虛擬函式定義為純虛函式。包含純虛函式的類叫
抽象類。抽象類不能定義對象,但可以定義指向它的
指針。
歷史
傳統的
結構化分析與設計開發方法是一個線性過程,因此,傳統的結構化分析與設計方法要求現實系統的業務管理規範,處理數據齊全,用戶能全面完整地其業務需求。
傳統的
軟體結構和設計方法難以適應軟體生產自動化的要求,因為它以過程為中心進行功能組合,軟體的擴充和復用能力很差。
對象是對現實世界實體的模擬,因面能更容易地理解需求,即使用戶和分析者之間具有不同的教育背景和工作特點,也可很好地溝通。
區別
面向對象的開發和傳統過程的開發的要素有:對象識別和抽象、封裝、
多態性和繼承。
對象(Object)是一個現實實體的抽象,由現實實體的過程或信息牲來定義。一個對象可被認為是一個把數據(屬性)和程式(方法)封裝在一起的實體,這個程式產生該對象的動作或對它接受到的外界信號的反應。這些對象操作有時稱為方法。對象是個動態的概念,其中的屬性反映了對象當前的狀態。
類(Class)用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的實例。
由上分析不難看出,儘管OOP技術更看中用戶的對象模型,但其目的都是以編程為目的的,而不是以用戶的信息為中心的,總想把用戶的信息納入到某個用戶不感興趣的“程式對象”中。
優缺點
· OOP 的優點:使人們的編程與實際的世界更加接近,所有的對象被賦予屬性和方法,結果編程就更加富有人性化。
· OOP 的也有缺點,就 C++ 而言,由於面向更高的邏輯抽象層,使得 C++ 在實現的時候,不得不做出性能上面的犧牲,有時候甚至是致命的 ( 所有對象的屬性都經過內置多重
指針的間接引用是其性能損失的主要原因之一;不過,筆者的局限性在於未使用過 VC++ 外的
面向對象語言,所以不是十分肯定,哈哈,有人笑出來了… )。
在計算機速度飛速發展的今天,你可能會說,一丁點的性能犧牲
沒什麼大不了。是的,從面向對象的角度,使的編程的結構更加清晰完整,數據更加獨立和易於管理,性能的犧牲可以帶來這么多的好處,沒有理由不做穩賺的生意吧?
不過,在某些對速度要求極高特殊場合,例如你做的是電信的交換系統,每秒鐘有超過百萬的人同時進行電話交換,如果,每一個數據交換過程都是一個對象,那么總的性能損失將是天文數字!!
或者這個例子不夠貼身,再舉個例子吧。假如你受聘於一個遊戲設計公司,老闆希望做出來的遊戲可以更多的兼顧到更多的電腦使用者,遊戲每秒鐘的運行的幀可以更多,子彈和爆炸物可以更多、更華麗。那么,你會發現使用 C++ 會使你的程式變得笨拙,無法滿足你的需求,除非你非得要你的遊戲運行於奔騰四的機器上 ( 如果不是,而你又堅持用 C++ 的對象編程,那么請減少主角的槍的威力吧 )。
如果你是冥頑不靈的人,你說不相信 OOP 會有性能上的損失,那么,我記得曾看到在 CSDN 上關於 VB 和 VC 執行效率的討論的文章,講述的就是使用了 MFC 以後,執行效率甚至低於 VB 開發出來的東西。請各位驗證一下:如果使用的是純粹的 C 語言語法的話,那么一定會比在 VB 編出來的東西要快很多 ( GetTickCount 函式可以查閱 MSDN ,如果想更加精確一些,可以使用 QueryPerformanceCounter 函式 )。
未來
(撰文/Bjarne Stroustrup & Tim Lindholm 編譯/
孟岩)
在未來三年,程式設計師編寫代碼的方式會發生哪些變化?
Stroustrup: 在C++中,假如沒有合適的庫在背後支撐,完成任何重要的工作都可能是很複雜的。而一旦有了合適的庫,任何東西都可以被我們操控於股掌之間。因此,構造和使用
程式庫的重要性與日俱增。這也暗示我們,
泛型程式設計(generic programming)將會越來越多地被運用。只有通過GP,我們才能確保庫的通用性和高效率。我還預期在分散式計算和“組件(components)”套用領域會出現喜人的增長。就大部分程式設計師而言,通過使用方便適用的程式庫,這些開發工作會變得簡單明了。
現在有一個趨勢,
編譯器廠商試圖把其特有的“對象模型”和圖形界面(GUI)細節推銷給用戶。比如微軟的COM和Inprise的
類屬性“properties”。對於用戶來說,這既不必要,也不情願。我所希望看到的
程式庫,應該是用標準C++打造,界面靈活,值得信賴的程式庫。通常,這些界面應該是平台無關的。C++的表達能力極強,即使不使用大量的宏,也應該足以達成這一要求。就算有些地方無法百分之百的遵守這一原則,也應該將對於平台和廠家的依賴性限制起來。這個目標的完成情況,可以反映軟體工具產業對於應用程式開發行業的關注程度。我懷疑目前對於那些獨立的、跨平台廠商來說,並不存在相應的市場。如果能夠建立這樣的市場,也許能夠促進廠商們為客戶做出“真正有用的”產品。
Lindholm: 對於編寫代碼的開發者來說,主要的驅動力量仍將是兩個:網路和分散式——也就是設計和開發非單機軟體的需求。大部分的應用程式將不會是孤零零地運行在單一設備上,而是運用了類似EJB和JSP之類技術的,平台無關的分散式程式。程式設計師們將不得不面對分散式計算的重重險阻。這將對許多程式設計師所依賴的設計模式、技術和直覺構成嚴峻的挑戰。這是選擇程式語言之前必須認識到的,儘管不同語言的設計特性可能促進或者阻礙這一轉化。
在網路套用的增長中,一個很重要的部分是小型移動設備和特殊Internet設備的爆炸性增長。這些設備各有各的作業系統,或者只在某種特定的設備領域內有共同的作業系統。我們現在還可以一一列舉出這些設備——家庭接入設備、
蜂窩電話、電子報紙、PDA、自動網路設備等等。但是這些設備領域的數量和深入程度將會很快變得難以估量。我們都知道這個市場大得驚人,PC的興起與之相比不過小菜一碟。因此在這些設備的應用程式市場上,競爭將會相當殘酷。獲勝的重要手段之一,就是儘快進入市場。開發人員需要優秀的工具,迅速高效地撰寫和調試他們的軟體。平台無關性也是制勝秘訣之一,它使得程式設計師能夠開發出支持多種設備平台的軟體。
我預期的另一個變化是,我們對於代碼(Java)和數據(XML)協同型應用程式的開發能力將會不斷提高。這種協同是開發強大應用程式的核心目標之一。我們從XML的迅速流行和ebXML規範的進展中,已經看到了這個趨勢。ebXML是一個針對電子商務和國際貿易的,基於XML的開放式基礎構架,由聯合國貿易促進和
電子商務中心(UN/CEFACT)與結構性信息標準推進組織(OASIS)共同開發。
我們能否期望出現一個真正的
面向組件(component-oriented)的語言?它的創造者會是誰呢?
Stroustrup: 我懷疑,這個領域中之所以缺乏成果,正是因為人們——主要是那些非程式設計師們——對“組件”這個意義含糊的字眼寄予了太多的期望。這些人士夢想,有朝一日,組件會以某種方式把程式設計師趕出歷史舞台。以後那些稱職的“設計員”只需利用預先調整好的組件,把滑鼠拖一拖放一放,就把系統組合出來。對於軟體工具廠商來說,這種想法還有另一層意義,他們認為,到時候只有他們才保留有必要的技術,有能力編寫這樣的組件。
這種想法有一個最基本的謬誤:這種組件很難獲得廣泛歡迎。一個單獨的組件或框架(framework),如果能夠滿足一個應用程式或者一個產業領域對所提出的大部分要求的話,對於其製造者來說就是划算的產品,而且技術上也不是很困難。可是該產業內的幾個競爭者很快就會發現,如果所有人都採用這些組件,那么彼此之間的產品就會變得天下大同,沒什麼區別,他們將淪為簡單的辦事員,主要利潤都將鑽進那些組件/框架供應商的腰包里!
小“組件”很有用,不過產生不了預期的槓桿效應。中型的、更通用的組件非常有用,但是構造時需要非同尋常的彈性。
在C++中,我們綜合運用不同共享形式的類體系(class hierarchies),以及使用templates精心打造的接口,在這方面取得了一定的進展。我期待在這個領域取得一些有趣和有用的成果,不過我認為這種成果很可能是一種新的C++
程式設計風格,而不是一種新的語言。
Lindholm: 編寫
面向組件的應用程式,好像更多的是個投資、設計和程式設計師管理方面的問題,而不是一個程式語言問題。當然某些語言在這方面具有先天優勢,不過如果說有什麼魔術般的新語言能夠大大簡化組件的編寫難度,那純粹是一種誤導。
微軟已經將全部賭注押在C#上,其他語言何去何從?
Stroustrup: C++在下一個十年里仍然將是一種主流語言。面對新的挑戰,它會奮起應對。一個創造了那么多出色系統的語言,絕不會“坐視落花流水春去也”。
我希望微軟認識到,它在C++(我指的是ISO標準C++)上有著巨大的利益,C++是它與IT世界內其他人之間的一座橋樑,是構造大型系統和
嵌入式系統的有效工具,也是滿足高性能需求的利器。其他語言,似乎更注重那些四平八穩的商用程式。
競爭
C#會不會獲得廣泛的接受,並且擠掉其他的語言?
Lindholm: 通常,一種語言既不會從別的語言那裡獲利,也不會被擠掉。那些堅定的Fortran程式設計師不還用著Fortran嗎?對於個人來說,語言的選擇當然因時而異,但就整體而言,語言的種類只會遞增,也就是說,它們之間的關係是“有你有我”而不是“有你沒我”。
對於一個新語言的接受程度,往往取決於其能力所及。Java技術被迅速接受,原因是多方面的,Internet和World Wide Web接口,在其他技術面前的挫折感,對於Java技術發展方向的全面影響能力,都是原因。另一個重要的原因是Java獨立於廠商,這意味著在兼容產品面前可以從容選擇。
C#是否會獲得廣泛接受?視情況而定。總的來說,那些對於平台無關性和廠商無關性漠不關心的程式設計師,可能會喜歡C#。那些跟微軟平台捆在一起人當然可能想要尋找VB 和VC的一個出色的替代品。但是對於程式跨平台執行能力特別關注的程式設計師,將會堅守Java之類的語言。這種能力對於多重訪問設備(multiple access devices)和分散式計算模型至關重要,而
Java語言提供了一個標準的、獨立於廠商運行時環境。
Stroustrup:C#的流行程度幾乎完全取決於微軟投入的資金多少。看上去C#的興起肯定會犧牲掉其他一些語言的利益,但是事實上未必如此。Java的蓬勃發展並沒有給C++帶來衰敗。C++的套用仍然在穩定增長(當然,已經不是爆炸性的增長了)。也許其他的語言也還能獲得自己的一席之地。
不過,我實在看不出有什麼必要再發明一種新的專有語言。特別是微軟,既生VB,何需C#?
優劣勢
Stroustrup: C++的優點自始至終都是這么幾條:靈活、高效,而且並非專有語言。現在ISO C++標準的出現,鞏固了最後一點。
我認為C++的高效是它最基本的優點。這種高效來自於其特有的數據和計算模型,較之Java和C#,這種模型更加貼近機器。不過,哪些程式才真正地渴望這么高的效率?這是個問題。我認為這類程式非常多。人們對於計算機的期望,永遠都超越硬體科技的發展速度。很顯然,Java和C#的設計者的想法不同,他們認為,在很多地方效率問題無關緊要。
C++主要的缺點,歸罪於糟糕的教育(是那些始終認為C++是個純粹
面向對象語言的人,和那些把C++當成C語言變體的人導致了這種情況),歸罪於不同平台上的不一致性,歸罪於不完整、不標準的
編譯器實現,歸罪於平台無關的系統級
程式庫的缺少。
這些問題歸於一點,就是缺乏一個卓越的廠商,能夠滿足整個C++社區的需求,勇於投入大量的資金開發必要的程式庫。
Lindholm: Java技術的成功,是因為它在合適的時間,出現在合適的地點,而且合理地選擇了語言和
計算平台的支持目標。Java並不是在所有場合都優於其他OOP語言,但是對於出現的新問題能夠解決得很出色。它面向Internet計算環境,避免了C++中晦澀的結構,成功翻越了繼承機制的惱人問題。垃圾收集機制顯著地提高了生產率,降低了複雜度。在網路背景下使用
虛擬機,以及有關安全性和動態載入的一系列設計選擇,迎合了正在出現的需求和願望。這些特性使Java不僅成為現有程式設計師的新武器,而且也為新的程式設計師創造了繁榮的市場空間。
此外,Java擁有一個標準化的、二進制形式的類庫,提供了必要的(當然並非充分的)平台與廠商無關性。平台與廠商無關性要求一項技術必須有清晰的規範,摒棄那些阻礙二進制標準實施的特性。C++雖然有一個ISO標準,但其實甚至對於相同系統與相同指令體系的各個平台,也提不出一個實用的、各版本兼容的二進制標準。
歷史上很多使用虛擬機的語言飽受責難,是因為其不夠出色的性能問題,而這要歸過於緩慢的
解釋器和糟糕的垃圾收集器。Java的早期實現也因為同樣的問題受到嚴厲的批評。但是自那時起,業界向新的虛擬機實現技術投入了大量資金,取得了顯著的效果,如今在大部分場合,Java的性能跟常規的
靜態編譯語言相比毫不遜色。這使得程式設計師在獲得平台和廠商無關性的同時,也不必付出性能上的代價。
C++並沒有強制使用
面向對象方法,因此為了編寫出色的面向對象代碼,就要求程式設計師們有相當強的紀律性。很多公司就是因為這個原因放棄了C++。作為語言,Java的一個突出的優點就是強制
面向對象方法,不允許非面向對象的結構。
C#介於C++和Java之間,腳踏兩隻船,因此既不夠安全,又失之複雜。
對於公司來說,採用新的語言要付出巨大代價。雇不到好的程式設計師(沒人熟悉這種新語言),培訓費用高得驚人,學習過程中生產率和產品質量下降,多年的經驗隨風消逝,等等。一種語言如何克服這些障礙?
Lindholm: 說得很對,採用新東西確實常常開銷巨大。不過問題是:這個新東西是否能夠節省更多的開支,或者提供巨大的改進,獲取合理的回報?很多公司發現,轉向Java技術不論在開發的後端(儘快進入市場、快速
疊代開發、維護簡單性)還是前端(跨平台發布,適用範圍從低端設備到高端伺服器的技術,安全性),都能節省大筆的開銷。
對於新事物的接納,常常是在痛楚的壓力之下。很大程度上,這正是Java所經歷的。Java的產生,是對當時很多系統的缺陷所做出的反應。Java技術通過下面的手段減輕了開發者的痛楚:1) 顧及了網路計算方面的需求,是應運而生。2) 在技術能力的抉擇上,保持良好的品位,顧及了大眾的心理。3) 採用適度強制性策略推行設計決定。此外,Java技術已經成為大學教學中的主流,這同樣保證了Java開發者隊伍的不斷壯大。
但是最重要的一點是,再沒有另一種
程式設計技術,能夠像Java那樣允許程式設計師開發基於Internet的不同平台之上的應用程式。
Java平台在這方面的傑出表現,已經被大量的實例證明。Java已經成為Internet上的預設應用程式平台,Java APIs也成為Internet應用程式開發的天然平台。
Stroustrup: 微軟和Sun把大筆的金錢扔在Java、VB和C#中,並不是因為他良心發現,也不是因為他們真的相信這些語言能夠帶給程式設計師更美好的生活,而是利益使然。
有一個說法,認為軟體工具廠商如果能夠把應用程式開發者的專業技術任務負擔起來,將獲取巨大的經濟利益。我對其背後的經濟分析頗為懷疑,我認為這很難成為現實,特別是當應用程式開發者使用開放的、標準化的工具時,他們可以有多種選擇,從而使上面的想法更加不可能。
多年以前,C++就已經具有
泛型能力(也就是templates和STL),有
運算符重載,有枚舉類型?我們會不會在Java的未來版本中看到這些特性?Java是不是應該納入這些特性呢?
Strousturp:從1988-89年起,C++就已經有了templates。但是我們花了不少時間來了解如何最好地運用這個工具,早期各廠家對於template的支持在品質上也有很大的差異。有些
編譯器廠商動作遲緩,至少有一個主要的編譯器廠商(好像是指微軟,微軟在
Visual C++4.0才開始支持template,在此之前一直聲稱template是過於複雜而又沒什麼用的技術,時至今日,Visual C++對於template的支持在主流編譯器中都屬於最差的一檔——譯者注)暗中鼓勵聲名狼藉的反template宣傳,直到他們自己終於學會了這項技術為止。直到今天,對於template的支持在品質上仍然有待改進。
你上面提到的那些特性,我認為Java(還有C#)應該,也肯定會逐漸引入。那些對於程式設計師來說最有用的語言特性和概念,將會逐漸集中,成為各家主流語言的必然之選。也就是說,我認為類似
析構函式和模板特殊化之類的機制,遠遠比枚舉等機制重要得多。
Lindholm:Java技術成功的原因之一,就是很清楚哪些不該做。我們得多問幾個為什麼:這項特性是不是必不可少?增加它會帶來哪些開銷?
運算符重載是C++中一項極其強大的特性,但是它也大大增加了C++語言的複雜度,很多人都難以招架。Java在各種可能的權衡之中,做出了明智的抉擇,找到了能力與需求之間的完美平衡點。
當然,Java也會發展,而且最重要的是,現在是開發者們在推動發展。Java增加
泛型能力這件事,很好地展示了Java是如何通過整個開發者社群的參與,在權衡中決定正確的平衡點。關於增加泛型類型(generic types)的“Java規格申請”(Java Specification Request, JSR)已經進入JCP(Java Community Process)程式,而且已經開發了很長一段時間。現在,在JCP中,有超過80個JSRs正在討論中,這充分體現了整個體系對開發者的積極反饋和高度合作,這正是驅動Java平台不斷進化的動力。
發展革新
(Evolution vs. Revolution)
C++是一種發展型的語言,Java和C#似乎更像是革新型語言(它們是從頭設計的)?什麼時候,革新型的語言才是必需的呢?
Lindholm: Java技術並非憑空出世,反而更像是發展型的。Java所有的特性,在Java平台推出之前,都至少已經存在於另一種環境之中。Java的貢獻在於,在眾多的特性和權衡中,做出了合理的選擇,使得產品既實用,又優雅。Java技術對於程式設計師的態度是:撫養,但不溺愛。
Stroustrup:從技術上講,我並不認為Java和C#是什麼“從頭設計的”革新型語言。倘若Java是從技術原則出發,從頭設計,大概就不會模仿C/C++那種醜陋和病態的語法了(不必驚訝,Stroustrup在很多場合表示過,C++採用C的語法形式,實在是迫於兼容性。他本人更偏愛Simula的語法——譯者)。
我認為,只有當程式設計師們面對的問題發生了根本的變化的時候,或者當我們發現了全新的、極其優越的程式設計技術,又完全不能為現存語言所支持的時候,我們才需要全新的語言。問題是,我們恐怕永遠也碰不到那些“根本”、“全新”的情況。
我以為,自從OOP問世以來,可稱為“根本”的新型程式設計技術,唯有
泛型程式設計(generic programming)和生成式程式設計(generative programming)技術,這兩項技術主要是源於C++ templates技術的運用,也有一部分曾經被視為
面向對象和函式式語言(functional languages)的次要成分,現在都變成正式、可用和可承受的技術了。我對於目前C++模板(template)程式設計的成果非常興奮。例如,像POOMA, Blitz++和MTL等
程式庫,在很多地方改變了數值計算的方式。
C#的一個“賣點”,就是它們的簡單性。現在Java是不是快失去這個賣點了?
Stroustrup:新語言總是宣稱自己如何如何簡單,對老語言的複雜性頗多非議。其實這種所謂的“簡單性”,簡單地說,就是不成熟性。語言的複雜性,是在解決現實世界中極為煩瑣和特殊的複雜問題的過程中逐漸增加的。一個語言只要活的時間夠長,總會有某些地方逐漸複雜起來,或者是語言本身,或者是程式庫和工具。C++和Java顯然都不例外,我看C#也一樣。如果一種語言能夠度過自己的幼年時代,它會發現,自己無論是體積還是複雜性都大大增加了。
Lindholm:Java技術的的功能在增加,需要學習的東西也在增加。不過功能的增加並不一定帶來複雜性的增加。Java技術的發展,並沒有使學習曲線更加陡峭,只是讓它繼續向右方延展了。
標準
標準化語言和開放型語言各自的優點和缺點何在?
Lindholm:對於一個開放、不允許專有擴展、具有權威的強制性標準語言或者運行環境來說,不存在什麼缺點。允許專有擴展就意味著允許廠商下套子綁架客戶。特別重要的是,必須讓整個平台,而不只是其中一部分完全標準化,才能杜絕廠商們利用高層次的專有API下套子。客戶要求有選擇廠商的自由,他們既要有創造性,又需要兼容性。
Stroustrup:對於一個語言,如C/C++來說,建立正式標準(如ISO標準)最大的好處,在於可以防止某一個廠商操縱這種語言,把它當成自己的搖錢樹。多個廠商的競爭給用戶帶來的是較低的價位和較好的穩定性。
專有語言的好處,一是流行,二是便宜(不過等你被套牢了之後,情況就會起變化),三是對於商業性需求可以做出快速的反應。
標準化語言的特點之一是,它不能忽略特殊用戶的需求。比如我在AT&T中所考慮的東西,其規模、可靠性和效率要求,跟那些普通廠商關注的大眾軟體相比,根本
不可同日而語。那些公司很自然只關注主要的需求。
然而,多數大機構和身處前沿的公司,都有著特殊的需求。C++的設計是開放、靈活和高效的,能夠滿足我所能想像的任何需求。跟其他的現代語言相比,C++的家長式作風可謂少之又少,原因就在這。當然,不能讚賞這一點的人會詬病C++的“危險”。
擁有正式和開放標準的語言主要是為編程工具的使用者和客戶服務的,而擁有專屬“標準”的語言,主要是為廠商服務的。