1. 程式人生 > >C/C++中,空數組、空類、類中空數組的解析及其作用

C/C++中,空數組、空類、類中空數組的解析及其作用

class 不同 理解 返回 free 而且 解析 分配 空類

轉自:http://blog.sina.com.cn/s/blog_93b45b0f01015s95.html

我們經常會遇到這些問題:

(1)C++中定義一個空類,他們它的大小(sizeof) 為多少?

(2)只有一個char數據成員的類的大小?

(3)能否定義一個空數組?

(4)空數組名做標示的指針指向什麽地方?

(5)空類有什麽用?

(6)空數組有什麽用?

等等......

這些問題,筆者在這篇文章統統做一個比較詳細的解析和認識。

1. sizeof是什麽?

首先我們要理解sizeof是什麽東西?

準確來講,對於C++這種強類型的語言,在某一時刻,對象的類型的大小是確定的,這個信息在編譯的時候直接可以確定,所以我們要明白sizeof並不是一個函數,而是一個返回對象類型大小的宏,這個宏的參數可以是對象,也可以是類型。

我們需要明白的是,在編譯結束後,sizeof的那個位置上面是直接被替換成一個常數的,我們用一個簡單而直觀的實驗來證實一下:

int main() {

int a=2;

int b[sizeof(a)];

cout<<sizeof(b)/sizeof(int)<<endl;

}

如果sizeof是作為的函數,那麽這個是不可能編譯成功的,因為棧上定義數組是一定需要常數(或常數表達式的)。

這樣我們就能知道:

sizeof(i++);

這樣一句話,i是不可能加1的,因為這句話在編譯的時候就已經被轉換成的對應的常數,而編譯的時候是不會進行運行的,這是常常出現的陷阱之一。

2. 空類的大小

很多人都知道,一個空類(後者是空類對象)使用sizeof的時候,結果是1。解釋相信大家也知道,想想,如果真是空類。那麽對於同一個空類的對象就不會占空間,不會占空間就意味著無法區分。

(有人可能會說,我們只分配對象名,不分配空間,那樣的話,你會讓整個C/C++語言為空類,以及相關的空類對象設計一套特殊的規則,是這個語言變得非常不合理)。

所以,C++的選擇是,自動的給空類插入一個char類型(只一個特殊對待),只要這個類對象將來占空間,那麽就可以通過地址來區分他們。

3. 空類的作用

還有一個關於空類的疑問就是,C++語言有必要保留空類嗎?空類實現空對象有什麽用?

有用的,尤其是在“泛型編程”中,空類(結構)的用處非常廣:

在其他的文章中提到,我們利用類型(通常是空類)來區別對待不同類對象的屬性。(其實我們是可以通過使用常數來區分的,但是區別我們很容易就能知道)。

使用常數來區分需要使用if else的這種運行時來確定執行的線路的方法,而使用函數重載的方法,在參數中加入一個空類域作為區分不同的函數的方法,編譯的時候直接選擇,而不是在運行的時候選擇,這是非常提高效率的。

要知道,不同的空類,是不同的。他們代表著不同的類型(雖然他們結構一樣)。在STL中,使用空類區分不同類型的標誌,從而在編譯的時候來對不同的類進行有針對性的優化是非常常見的。

template <typename A>

void fun(A a)

{

typedef typename trait<A>::type T;

_fun(A a, *(new T()));

}

template <typename A>

void _fun(A a, int)

{

……

}

template <typename A>

void _fun(A b, float)

{

……

}

當然,空類應該還有其他的用處。我們所有知道和理解的就是:空類是C++中一個有用的機制,不同名稱的空類代表著不同的類型。

空類在編譯的時候會被編譯器自動的加入一個char成員,不為別的,只是為了,讓它被實例後的對象占有空間,從而可以區分。

4. 空數組

C中我們可以定義空數組:

int a[0];

使用sizeof的時候你c猜是多少:

0

好吧,這裏0,我們可以理解。

但是問題就來了:

既然前面對於空類的情況中,因為需要讓對象唯一定位,所以插入char,那麽空數組既然sizeof的大小為0,那應該就是不占空間,那麽如何區分。

事實上,對於空數組,在C/C++有著特別的交代(可能是具體的實現不同,這裏只是使用GCC,G++)。

空數組名是一個指針,(但是又不占空間)指向一個位置:

對於結構體中,空數組名這個指針指向了前面一個成員結束的第一個空間。

對於非結構題中,空數組名這個指針指向的內容,與前一個對象的指針的內容一樣(雖然可能他們的類型不一樣)。

雖然,我們不知道這兩種安排有什麽玄機,或者益處,但是無所謂(下一節會介紹空數組的作用),但是針對空數組的sizeof為什麽可以為0,我們有了解釋。

與空類,不同的是,空數組是一個對象,而不是一個類。既然我們這個對象定義出來了,而且它會指向一個空間(雖然這個空間可能會與其他的地方重疊)但是,我們總算能夠區分開不同的空數組。

5. 空數組的作用

其實在C裏面,空數組的使用是非常多的。

問題:

假如你想要給一個結構體(代表一個功能)添加一個緩沖區。你會怎麽做?

1)定義一個固定長度的buffer數組成員,這樣的不好之處在於buffer會被定死。

2)定義一個buffer指針,在構造函數(雖然C沒有,但可以使用initialize函數來代替)中動態的創建一個需要大小的buffer,給這個結構體使用。

但是這樣就需要我們特別管理這個空間(使用析構函數),否則會很容易出現內存泄露。並且,一個buffer指針還占有了一個空間。

那麽C裏面,就有一個巧用空數組來達到這個問題的方法。

sttuct T

{

int a;

int b;

……

char buffer[];

};

我們知道,由於buffer並不占空間,所以,T的對象的總大小是不會把buffer算上的。

struct T * p=(T*)malloc(sizeof(T) +buffer_len);

看到沒有,在聲請空間的時候,加上buffer_len(所需緩沖區長度),這樣結構體和緩沖區一起被申請了,(在結束的時候,也可以直接使用free一起釋放,可以避免獨立的管理結構體和緩沖區)。

並且我們知道,buffer這個“指針”指向的就是結構體後面的那個buffer_len的空間。

這樣做還有一個好處,如果我們分開管理結構體和緩沖區(通常這個時候結構體是一個小碎片)。在申請和釋放的時候,很容易在內存中制造出碎片。

而如果向上面的這樣管理,結構體,依附這緩沖區(大塊)一起被管理。那將是一個很美好的事情。這就是空數組的用處。

6. 類中空數組

class T

{

int a[0];

};

您看這個類的sizeof為多少:

0

你會覺得這像個玩笑,但是其實仔細分析一下,也是可以所通的。

我前面說給一個空類插入一個char成員,是因為想讓程序能夠區分該類的不同對象。

而這裏,我們知道由於空數組是不占內存的,它就像一個指針指向某個地方,但是又不占內存。但是的確我們的類對象能夠借助空數組這個東西來區分開來。

所以既然目的達到了,那麽為什麽還要加入什麽一些什麽信息呢?

C/C++中,空數組、空類、類中空數組的解析及其作用