c++/java/c# 幾種程式設計語言的指標、引用比較

來源:互聯網
上載者:User

前一段時間,我在 cnblogs 別人的部落格中,談到:

java 中的引用/指標,與 c++/C# 中的引用/指標不是一個概念.

Java 引用,相當於 c++ 指標(fun3)。Java 引用可以賦值 null, 而 c++ 引用 (見 fun2) 不能賦值 null,c++ 指標可以賦值 null(fun3).

Java 中,無 c++ 引用(fun2)對應的文法。

 

結果引起不必要的質疑,特此,寫部落格,對c++/java/c# 幾種程式設計語言的指標、引用,進行比較,期望引起更多的人,對此有所關注。

從文法上看,三種開發語言中,C++ 的指標、引用,最為複雜,因此,下面的舉例,都從 C++ 代碼開始,然後與 java/c# 的文法進行比較。

 

1)  C++ 簡單類型變數,有直接變數定義、指標定義、引用定義。

    int aa = 10;//c++    int &bb = aa;//c++    int *cc = &aa;//c++

上述三行代碼,最後三個變數指向同一個資料。相比較而言,java/c# 都只有變數定義,無引用定義、指標定義。補充:感謝 xiaotie 、飛浪 的提醒:C#中是有指標的,在unsafe狀態下,可以定義和使用指標。特更正。

 

2) C++ 函數調用參數,簡單類型變數,有直接變數定義、指標定義、引用定義,後兩個,在函數內部改變資料,退出函數,能看到改變後的資料。

void simple_by_val(int a, const int b){    a=15;    //b=13;            //error C2166: l-value specifies const object    //a=NULL;        //good    //b=NULL;        //error C2166: l-value specifies const object}void simple_by_ref(int &a, const int &b){    a=25;    //b=23;            //error C2166: l-value specifies const object    //a=NULL;            //good    //b=NULL;        //error C2166: l-value specifies const object}void simple_by_pointer(int *a, const int *b){    *a = 35;    //*b = 33;        //error C2166: l-value specifies const object    a = NULL;        //ok    b = NULL;        //ok}

java 沒有這麼多名堂,只有直接變數定義。C# 略為複雜一點,有引用,有 out 參數。

        static void M(int a, ref int b, out int c)        {            c = 13;        }

相比較而言,C# 的函數參數( ref int b), 類似於C++的函數參數( int &a),都是調用函數前要賦初值,在函數內部改變資料,退出函數,能看到改變後的資料。

而 C# 的 (out int c),在 C++/Java 中,無對應的文法。這個可以調用函數前,不賦初值。在 C# 之前,也很少見到這種文法,只在一些資料庫的預存程序、函數定義中,見過類似文法。估計是從資料庫編程文法中抄襲過來的文法。

特別註明:C# 的引用( ref int b),只是用在函數參數變數定義上,不能用在函數內部的局部變數中。C++ 中的引用( int &a),可以用在函數內部的局部變數中。

 

3)  C++ 的類物件變數定義文法,較為複雜,可以定義在stack 上(不用 new),可以定義在 heap(用 new)。

    CMyClass obj;                        //stack    CMyClass *p2 = new CMyClass();        //heap

java/C# 中,沒有這麼複雜,可以認為是上述兩種“綜合+簡化”了。

 

4) 在 java/C# 中,如下用法是錯誤的,會報null 指標異常;但是在 C++ 裡是合法的。

    CMyClass obj;    obj.run();

在 C++ 中,

CMyClass obj;

以上一行代碼已經調用了建構函式,完成了變數初始化。而在 java/C# 中,這一行代碼相當於:

CMyClass obj = null;

 

5) C++ 中,stack 變數出了作用範圍,記憶體自動回收;heap 變數,需要手工 delete。

java/C# 中,變數是空閑時自動回收的(理論上的),不是變數出了作用範圍,就記憶體回收。

 

{    CMyClass obj;                        //stack    obj.test();}//此處, stack 變數自動被 delete ,記憶體自動回收{    CMyClass *p2 = new CMyClass();        //heap    p2->test();} //此處,超出變數 p2 的作用範圍,下面不能再用 p2 變數了,但是,記憶體並未釋放,有記憶體泄露。

 

 

 

 

6) 以下代碼在 C++ 中是正確的,在 java/C# 是錯誤的。在 java/C# 文法中,沒有定義變數加 * 的,也不能用 -> 來調用類的函數或類的成員變數,也不能用 delete。

 

CMyClass *p1 = null;CMyClass *p2 = new CMyClass();p2->ab();delete p2;p2 = null;

 

 

7) 以下代碼,在java/C# 文法中,是正確的,在 C++ 是錯誤的。C++ 中,這種賦值要用指標 (CMyClass *p1 = null;)。

CMyClass p1 = null;CMyClass p2 = new CMyClass();

 

8) 以下代碼,在 C++ 代碼中,會調用“拷貝建構函式”、"等號重載函數"。這兩個函數,在 C++ 中,預設會由編譯器自動產生。

    //C++
    CMyClass obj; //調用建構函式 CMyClass obj2 = obj; //調用拷貝建構函式 obj2 = obj; //調用 = 操作符重載函數

以上代碼,大致相當於 java/C# 中的 複製"clone"。但更隱蔽(初學者不知道調用了 C++ 建構函式、拷貝建構函式、= 操作符重載函數)、更複雜。java/C# 無操作符重載函數。

//C#            CMyClass obj = new CMyClass();            CMyClass obj2 = (CMyClass)obj.Clone();

而在 C# 中,Clone 函數並不會自動產生。在 Java 中,可以調用 super.clone() ---- Java 基類 Object 預設有一個 clone 函數。

在 C++ 中,預設會由編譯器自動產生“拷貝建構函式”、"等號重載函數",這一點,很多時候會造成問題,要特別注意。

在 C++ 中,函數傳回值不要用 CMyClass ,這會造成不必要地調用“拷貝建構函式”、"等號重載函數";也不要返回引用 CMyClass&, 對函數內局部變數的引用,退出函數後無法繼續使用。而要返回指標 CMyClass *(最好用智能指標封裝後的指標變數)。這一點很多初學者不明白。

但是 C++ 的 std:string 除外。std:string 的“拷貝建構函式”、"等號重載函數"經過最佳化,拷貝後的變數,與拷貝之前的變數,內部使用相同的 char[] 數組,只有當一個 string 變數改變時,才會把 char[] 數組複製成兩份。std:string 的“拷貝建構函式” 沒有效能上損失,又比 string 指標減少了記憶體泄露,因此,對 std:string ,使用時盡量用 物件變數、對象引用、對象拷貝構造,避免使用 std:string 指標。

另,java/C# 的 String 變數不可改變(有其它類,比如 java StringBuilder類是可變的),C++ 的 string 變數可以改變。這個細微差異,很多人不明白。

 

9) C++ 引用文法,有一些是 Java/C# 程式員不知道的文法:

//C++CMyClass &a1; //錯誤,C++ 引用變數定義的時候就要初始化;//Java/C# 物件變數,沒有要求變數定義的時候,就要初始化CMyClass &a1 = NULL; //錯誤,C++ 引用變數不能賦值 nullCMyClass &a1 = new CMyClass(); //錯誤,C++ 引用變數不能賦值給一個 new 對象,這種情況,要用 C++ 指標。//以下C++ 代碼是正確的:CMyClass a;CMyClass &a1 = a;CMyClass *b =new CMyClass();CMyClass &b1 = *b; //這種寫法不常用。

 

 

10) Sun 自稱 java 中消滅了 C++ 中萬惡的指標,自己的物件變數,都是引用。做個比較:

C++ 引用不能賦值 null, 不能賦值 new XXX();C++ 指標可以賦值 null, 可以賦值 new XXX()。

C++ 引用對象通常在 stack 中,而C++ 指標 new 出來的對象則在 heap 中。

 

java/C# 中的物件變數,可以賦值 null, 可以賦值 new XXX()。java/C# 中的物件變數在 heap 中。

 

因此,java/C# 中的物件變數,更像是 C++ 中的指標,而不是 C++ 中的引用。

 

11) C++ 中,指標變數是一個 long 型整數,可以亂指的:

CMyClass *obj = (CMyClass *) 99;        //compile/run good, should not use    

如果我知道一個記憶體位址,就可以定義一個C++指標變數,指向這個記憶體位址。C++ 的“引用”沒有這個功能。C#/Java 的物件變數更沒有這個功能。

“指標亂指” 是 C++ 指標功能強大、靈活的體現(PC 上最早出現播放視頻的時候,大概是 intel 486 CPU 時代,C++軟體通常都直接寫顯存,據說這樣速度更快),也是最容易出問題的地方。估計是因為這個原因,所以C#/Java 都去掉了這個功能。所謂“萬惡的C++指標”,多半,也是指的是“指標亂指”。

 

12) C++ 有野指標,即已經刪除對象,但指標還是指向刪除對象,還可以繼續操作,但運行結果不保證正確。

CMyClass *p = new CMyClass();...//給 p 指向的記憶體賦值delete p;//這時 p 仍然指向之前的記憶體位址,該記憶體位址資料,一般情況下、短時間內,並沒有被清空或者覆蓋,仍然可以讀/寫。這就是“野指標”。p->run(); //運行結果可能正確,可能不正確,沒有保證。//此時指標 p 對應的記憶體,可能被下一個 new XXX() 代碼,用了這個記憶體,因此,理論上講,delete 之後的指標,不應再用來操作對象。p= NULL; //將指標指向“空”,可以避免“野指標”問題。p->run(); //這裡會報執行階段錯誤。也就是null 指標異常。null 指標異常在 java/c# 中都有。

C++ 中,delete 與將變數賦值 null , 理應放在一起,可以認為是一個“資料庫事務”一樣的,要麼都成功、要麼都失敗。其實,delete 關鍵字,是由 C++ 標準定義的,標準中,完全可以要求: delete 所在行的代碼,執行之後,把指標變數變成 null(C++ 標準的規範,很多都是規定編譯器做什麼,因此可以加這個規定)。這樣可以避免野指標問題。可惜,C++ 標準,在這方面沒有考慮周全。

 

另,有人抱怨,面試做題,看不是是 C++ 還是 Java、C# , 期望通過看本文,可以協助一二。

---------------------------------------

歡迎大家下載試用折桂單點登入系統, http://zheguisoft.com

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.