從C++到java

KingsLanding發表於2014-05-01

  C++和java都號稱是物件導向的語言,雖然C++不完全算是。學習過C++如何快速對java有個大體的掌握,可以通過對比來進行了解。

  首先還是來高大上一下,看看他們的使命:

  · C++ 被設計成主要用在系統性應用程式設計上的語言,對C語言進行了擴充套件。對於C語言這個為執行效率設計的過程式程式設計語言, C++ 特別加上了以下這些特性的支援:靜態型別的物件導向程式設計的支援、異常處理、RAII以及泛型。另外它還加上了一個包含泛型容器和演算法的C++庫函式。
  · Java 最開始是被設計用來支援網路計算。它依賴一個虛擬機器來保證安全和可移植性。Java 包含一個可擴充套件的庫用以提供一個完整的的下層平臺的抽象。Java 是一種靜態面嚮物件語言,它使用的語法類似C++,但與之不相容。為了使更多的人到使用更易用的語言,它進行了全新的設計。

   總體的瞭解一下java和C++在語義上的區別:

         ·C++ 允許給函式/方法的引數設定預設值, Java 不提供這個特性. 但是方法過載可以達到同樣的效果.

         ·C++ 裡最小的編譯單位是一個函式; Java 裡最小的編譯單位是一個類. 在 C++ 裡, 函式可以被單獨編譯. 在 Java 裡, 要編譯和維護單獨的方法需要把它們移到超類或子類或者使用其他的程式碼重構的技巧.

         ·C++ 允許基本型別之間的一些隱式的轉換, 也允許程式設計師對於使用者自定義型別相關的隱式轉換規則. 在 Java 裡, 只有基本型別之間變寬型別的轉換可以是隱式的; 其餘的轉換需要顯式的型別轉換語法.

         ·這造成的一個後果是,雖然在 Java 和 C++ 裡迴圈的條件(if, while 和 for 裡的退出條件)預期的都是一個布林表示式, 但 if(a = 5) 這樣的程式碼在 Java 裡會導致編譯錯誤,因為沒有從整型到布林的隱式變窄轉換. 如果程式碼是 if(a == 5) 的輸錯的情況那麼是很方便發現這個錯誤的. 而目前的 C++ 編譯器一般來說只會針對這種情況產生一個警告.

         ·對於傳引數給函式的情況, C++ 支援引用傳遞和值傳遞. 在 Java 裡, 引數總是值傳遞的. 但在 Java 裡,所有的非基本型別的值都只是對於物件的引用 (用 C++ 的術語來說, 它們是智慧指標). 物件在 Java 裡不是作為值直接被使用的,只有物件的引用可以被直接操作; 習慣於將物件當做值直接使用的 C++ 開發者經常會把這個跟引用傳遞搞混.

         ·Java 內建的型別在位元組寬度和取值範圍上是被虛擬機器定義好的; 在 C++ 裡, 內建的型別有定義一個最小取值範圍, 但是其他的部分(位元組寬度)可以被對映成具體平臺上支援的原生型別.

         ·舉個例子, Java 字元是16位的Unicode字元, 字串是由這樣的字元組成的序列. C++ 提供窄和寬兩種字元,但實際的字元寬度是和平臺相關的, 視所用的字符集而定. 字串可以由這兩種字元中的一種組成.

         ·浮點數及其操作的精度和舍入方式在 C++ 裡是平臺相關的. Java 提供了一個可選的嚴格的浮點數模型,保證跨平臺的一致性,不過可能會導致執行時效率比較差.

         ·在 C++ 裡, 指標可以作為記憶體地址直接操作. Java 沒有指標 — 它只有物件引用和陣列引用,這兩者都不允許直接用來訪問記憶體地址. 在 C++ 裡可以構造一個指向指標的指標,而 Java 的引用只能指向物件.

         ·在 C++ 裡, 指標可以指向函式或者方法(函式指標). 在 Java 裡的等價物是物件或者介面的引用.

         ·雖然有使用棧記憶體分配的物件, C++ 還是支援區域資源管理, 一個用來自動管理記憶體和其他系統資源的技術,此技術支援確定性物件銷燬(deterministic object destruction). 不過,區域資源管理在 C++ 裡是不被保證的;它只是一個設計模式,所以需要依賴程式設計師遵守相關的規則. Java 通過使用垃圾蒐集來支援自動記憶體管理,但對於其他的系統資源(視窗,通訊埠,執行緒),如果垃圾蒐集器無法決定它們是否不再被用到,那通常還是需要顯式的釋放的.

         · C++ 的使用者可自定義操作符過載的特性在 Java 裡是不支援的. 唯一在 Java 裡可以過載的操作符是 "+" 和 "+=" 操作符, 在字串裡過載為連線字串.

         ·Java 的標準應用程式介面支援反射和動態載入任意程式碼.

         ·C++ 支援靜態和動態的庫連線.

         ·Java 支援泛型, 其主要目的是提供型別安全的容器. C++ 支援模板, 在泛型程式設計方面提供了更強的支援.

         ·Java 和 C++ 都對基本型別(也叫"內建"型別)和使用者自定義型別(也叫"複合"型別). 在 Java 裡, 基本型別只有值的語義,複合型別只有引用的語義. 在 C++ 裡所有的值都有值語義,可以建立對於任何型別的引用,這樣就允許通過引用語義來操作物件.

         ·C++ 支援任意型別的多重繼承. 在 Java 裡一個類只能從單個的類繼承而來,但一個類可以實現多個的介面(換句話說,它支援型別的多重繼承,但對於實現只能單繼承(it supports multiple inheritance of types, but only single inheritance of implementation))。

         ·Java 對於類和介面是顯式區分的. 在 C++ 裡多重繼承和純虛擬函式使得定義出類似於 Java 的介面的類是可能的,不過會有少許區別.

         ·Java 在語言和標準庫都對多執行緒有良好的支援. synchronized 這個 Java 的關鍵字為了支援多執行緒應用提供了簡單而安全的互斥鎖 ,但同步(synchronized)區只能用 LIFO 的順序離開. Java 也為更高階的多執行緒同步提供了健壯而複雜的庫. 在 C++ 裡沒有專門為多執行緒定義的記憶體模型; 但第三方庫提供了和 Java 差不多的功能; 不過這些 C++ 庫之間差異較大,一致性不好.

         ·C++ 方法可以宣告為虛擬函式, 虛擬函式是在執行期根據物件的型別才確定的. C++ 方法預設情況下不是虛的. 在 Java 裡, 方法預設情況下是虛的, 但可以使用final關鍵字使之宣告為非虛的.

         ·C++ 列舉屬於基本型別,支援和其他整數型別之間的轉換和比較. Java 列舉實際上是類的例項(它們從 java.lang.Enum<E> 擴充套件而來),象其他的類一樣可以定義建構函式,資料成員及方法.

 

  看完上面這些可能與實際的語言語法比較脫離,比如什麼“反射”這種動態語言的特性在C++裡面是不存在的,下面來看一下簡單的總結:

main 函式
C++


int main( int argc, char* argv[])
{
  printf( "Hello, world" );
}
Java
// 每個函式都必須是一個類的一部分;當java <class>執行是一個特定類的主函式會被呼叫
// (因此你可以讓每個類都有一個main函式,這在寫單元測試是很有用)
class HelloWorld
{
  public static void main(String args[])
  {
    System.out.println( "Hello, World" );
  }
}

 

類的宣告
除了 Java 不要求用分號外幾乎是相同的。 
C++
    class Bar {};
    
Java
    class Bar {}
   

方法宣告
  都相同的, 除了在Java,方法必須總是 某個類的一部分並且可能public/private/protected 作為修飾


建構函式和解構函式
  建構函式都是相同的 (即類的名字), Java沒有準確意義上的的解構函式

靜態成員函式和變數
方法宣告是相同的, 但 Java 提供靜態初始化塊來來初始化靜態變數 (不需要在原始檔中宣告): 
class Foo 
{
  static private int x;
  // 靜態初始化塊
  { x = 5; }
}物件的宣告C++  // 在棧中

  myClass x;
 
  //或者在堆中
  myClass *x = new myClass;
    
Java
  // 總是在對堆中宣告
  myClass x = new myClass();
 

繼   承
C++
  class Foo : public Bar
  { ... };
    
Java
  class Foo extends Bar
  { ... }
    

訪問級別 (abstraction barriers)
C++

  public:
    void foo();
    void bar();
    
Java
  public void foo();
  public void bar();
    

虛擬函式
C++
  virtual int foo(); // 或者非虛擬函式寫作 int foo();
    
Java
  // 函式預設的就是虛擬函式; 用final關鍵字防止過載
  int foo(); // 或者, final int foo();

 

記憶體管理
  大體上是相同的--new 來分配, 但是 Java沒有 delete,因為它有垃圾回收器。

NULL vs null
C++
    // 初始化一個指標為 NULL
    int *x = NULL;
    
Java
   // 編譯器將捕獲使用未初始化的引用
   //但是如果你因需要初始化一個引用而賦一個null,那麼這是無效的
    myClass x = null;
    

布林型
  Java有一點羅嗦: 你必須寫 boolean而不止是 bool. 
C++
  bool foo;
Java
  boolean foo;

常   量
C++
  const int x = 7;
    
Java
  final int x = 7; (final 關鍵字修飾類的方法的時候表示該方法不可被子類重新定義,因為在java中的方法都是預設virtual的)
    

拋異常
  首先,Java在編譯器強制拋異常—如果你的方法可能會拋異常你必需明確報告 
C++
  int foo() throw (IOException)
Java
  int foo() throws IOException

數     組
C++
  int x[10];
  // 或
  int *x = new x[10];
  // 使用 x,然後歸還記憶體
  delete[] x;
    
Java
  int[] x = new int[10];
  // 使用 x, 記憶體有垃圾回收器回收或
  //或在程式生命週期盡頭歸還給系統
    

集合和迭代器
C++

迭代器是類的成員。範圍的開始是<容器>.begin(), 結束是 <容器>.end()。 用++ 操作符遞增,  用 *操作符訪。 
  vector myVec;
  for ( vector<int>::iterator itr = myVec.begin();
     itr != myVec.end();
    ++itr )
  {
    cout << *itr;
  }
    
Java
迭代器只是一個介面。 範圍的開始是 <集合>.iterator,你必須用itr.hasNext()來檢視是否到達集合尾。 使用itr.next()(是在C++中使用操作符++ 和*操作的結合)來獲得下一個元素。 
  ArrayList myArrayList = new ArrayList();
  Iterator itr = myArrayList.iterator();
  while ( itr.hasNext() )
  {
     System.out.println( itr.next() );
  }
 
  // 或, 在Java 5中
  ArrayList myArrayList = new ArrayList();
  for( Object o : myArrayList ) {
    System.out.println( o );
    }
    
 

    
抽象類
C++
  // 只需要包含一個純虛擬函式
  class Bar { public: virtual void foo() = 0; };
    
Java
  // 語法上允許顯示的宣告!
  abstract class Bar { public abstract void foo(); }
 
  // 或者你也可以宣告一個介面
  interface Bar { public void foo(); }
 
  // 然後讓一個類繼承這個介面:
  class Chocolate implements Bar
  {
    public void foo() { /* do something */ }
  }
    
引用 vs  指標
C++
  //引用不可改變,通過使用指標來獲得更多的靈活性
  int bar = 7, qux = 6;
  int& foo = bar;
    
Java
  // 引用是可變的,僅儲存物件地址; 
  //沒有指標型別
  myClass x;
  x.foo(); // error, x is a null ``pointer''
 
  // 注意你要總是用 . 來訪問域,也就是說不會出現 ->
    
注   釋
兩種語言是一樣的 (// 和 /* */ 可以用) 

 

 

相關文章