天天看點

C++ : 從棧和堆來了解C#中的值類型和引用類型

C++中并沒有值類型和引用類型之說,标準變量或者自定義對象的存取預設是沒有差別的。但如果深入地來看,就要了解C++中,管理資料的兩大記憶體區域:棧和堆。

棧(stack)是類似于一個先進後出的抽屜。它的體積是有限的,一般為2M左右。

而堆(heap)則相對來說體積可以很大,這一般跟計算機的虛拟記憶體設定有關系。

棧中存取對象的記憶體是自動回收的,用完即銷毀了,一般方法内部的變量和參數都是通過棧來存取的(但也正因為如此,它們的生命周期很短)。但它的問題是,體積有限。

一些大的對象,我們可能要通過堆來建立它。程式員可以控制這些對象什麼時候建立,什麼時候銷毀。這無疑帶來了靈活性,也同時帶來了一些風險,事實上,相當一部分的程式的崩潰都是因為不恰當地使用了堆,以及沒有及時清理在堆上申請的記憶體。或者反過來說,可能會清理多次(這也會導緻崩潰)。

通常來說,如果希望某個對象或者變量的生命更長一些,也可以将其作為全局變量或者靜态變量。但那樣又導緻了它們必須等到程式結束才會釋放。

下面我用一個例子來示範一下這個問題

#include <iostream>

using namespace std;

class human{

public:

    void Talk();

    ~human(){cout<<"析構函數在工作..."<<endl;}

private:

    int age;

};

void human::Talk(){

    cout<<"Hello"<<endl;

}

int main()

{

    human h;//建立一個human對象,這個對象就生存在棧上,它所需的大小是根據其成員決定的

    cout<<"h的大小為:"<<sizeof(h)<<endl;

    cout<<"h的位址是:"<<&h<<endl;

    h.Talk();

    human *p=new human();//通過new關鍵字,是在堆上面建立一個對象,它所申請的空間也是内部成員決定的.這裡也是4

    cout<<"p的大小為:"<<sizeof(p)<<endl;

    cout<<"p的位址為:"<<p<<endl;

    p->Talk();

    delete p;

    //删除p這個指針指向的堆上面的記憶體.如果用完該對象,我們不删除,那麼該記憶體就一直存在,并不會自動删除.這就稱為記憶體洩漏.

    //如果類型定義了析構函數,此時将調用它(反之,如果一個對象是在堆上建立的,那麼除非調用delete語句,否則析構函數不會運作

    cout<<"p的位址為:"<<p<<endl;//我們隻是删除了該塊記憶體上面的資料,位址還是存在的

    //建議在删除p之後,将其置為0

    //p=0;

    //delete p;//但如果再次删除,又會發生崩潰,因為該記憶體已經沒有了.

    return 0;

}

​​

C++ : 從棧和堆來了解C#中的值類型和引用類型

​​

有兩句“析構函數在工作”,第一句是p這個指針所指向的堆上面那個對象的析構,而最後那句在是h這個在棧上的對象的析構。

有興趣的朋友,可以将delete語句注釋掉,則隻會看到一個析構過程。

我們可以做得更加複雜一些來看

    cout<<"p的位址為:"<<p<<endl;

    p->Talk();

   human *p2=p;//建立另外一個指針,讓他也儲存一樣的位址。

    cout<<"p2的大小為:"<<sizeof(p2)<<endl;

    cout<<"p2的位址為:"<<p2<<endl;

    p2->Talk();    delete p;

   //delete p2;//不光是p不能在删除,是以相同的指針都不能再删除了,否則就崩潰了    return 0;

知道了上述原理之後,我們就不難了解C#中的值類型和引用類型了。

  • 值類型是指基礎資料類型(除了string),結構體,枚舉
  • 引用類型是指類,接口,委托(其實類似指針),string,object

值類型是生存在棧上,好處是效率高,不需要額外的回收。但它的空間是有限的,所有一般隻适合基礎類型(char,byte,int,short,long,bool,double,float等)。

引用類型則生存在堆上,但在棧上有一個指針(因為在堆上的對象都是匿名的,指針此時起到了一個别名的作用,其實就是等同于引用的概念,固有引用之說)

引用類型既然生存在堆上,那麼按照C++的情況就必須我們自己去銷毀它。但從上面的示範就不難看出,何時銷毀它,以及會不會忘記銷毀它,或者重複銷毀它,都是一個很大的難題。是以在.NET Framework中,通過CLR中垃圾回收器(GC)來負責回收。一般的程式員不需要特别在意這個過程。

最後有意思的是,C#中的INT,其實并不等同于C++中的INT,大家如果有興趣的話,可以看一下,它其實是一個結構體。

繼續閱讀