2010-05-13 64 views
9

兩個引號++標準,§1.8:空基類優化從C

一個目的是存儲的區域。

基類子對象的大小可能爲零。

我不認爲一個存儲區域的大小可以是零。這意味着某些基類子對象實際上不是對象。這些陳述如何共存?

+1

爲什麼你認爲一個存儲區域的大小不能爲零?顯然它可以:) – 2010-05-13 12:24:36

+1

調用malloc(0)。 – bmargulies 2010-05-13 12:27:48

+1

@bmargulies'malloc'在C語言中特別措辭,因此它不需要創建一個零大小的對象:「如果請求的空間大小爲零,則行爲是實現定義的:或者返回空指針,或者行爲就好像大小是非零值「 – 2010-05-13 12:37:50

回答

10

關於「區域」定義的哲學論證是不必要的。 「除非是位域,否則大多數派生對象的大小應該不爲零......基類子對象的大小可能爲零」。

因此,標準很清楚什麼對象(以及哪些「存儲區域」)可以具有零大小。如果你不同意這個標準,「區域」在英語中意味着什麼是一回事,你可以對作者的(與編程無關的)文學技能作出錯誤的判斷。對於這個問題,你可以錯過他們的詩歌技巧(14.7.3/7)。但是,這個標準在這裏所說的關於類類型對象的大小很清楚。

閱讀標準的實用方法是,如果給出兩個合理的單詞解釋,選擇一個與標準的同一部分中的另一個句子不直接相抵觸的單詞。不要選擇更貼近您個人喜歡使用的單詞,甚至是最常見的用法。

+0

「除非是位域,否則大多數派生對象應該有一個非零大小」似乎與被稱爲「分配一個沒有元素的數組」的new int [0]衝突。這意味着在這種情況下數組實際上* do *包含內部填充嗎? – 2010-05-13 12:45:45

+0

我不認爲數組是一個派生最多的對象。 1.8/4:「如果一個完整的對象... ...是類類型,它的類型被認爲是最派生類......最派生類類型的對象被稱爲最派生類對象」 – 2010-05-13 12:47:48

+0

@Steve它說「大多數派生類類型或非類類型的對象稱爲最派生對象「。 - 編輯:C++ 03的措辭似乎與FCD措辭不同。 – 2010-05-13 12:50:19

9

C++不允許大小爲零的對象,因爲每個對象都必須具有唯一的內存地址。所以,如果您有:

struct empty {}; 

// ... 

empty myempty; 
empty* ptr = &myempty; 

然後ptr必須指向一個唯一的內存地址。該標準規定爲此目的,對象的最小尺寸爲1個字節。類似地,允許分配大小0,並返回一個有效的指針,即使不允許寫入該指針(這適用於malloc(0),並且new empty返回指向一個字節的指針,因爲sizeof(empty) == 1)。

如果我從empty得到像這樣:

struct derived : public empty 
{ 
    int data; 
} 

不再有基類empty佔用一個字節的任何一點,因爲所有derived都會有一個唯一的地址,由於data成員。在這種情況下,引用「基類子對象可能具有零大小」以允許編譯器不爲empty使用任何空間,例如sizeof(derived) == 4。正如標題所述,這只是一種優化,對derivedempty部分佔用零空間是完全合法的。

+0

實際上'sizeof(空)'被允許更大,並且大多數編譯器至少會使它的機器字寬。 – 2010-05-13 13:18:53

+0

「每個對象都必須具有唯一的內存地址」 - 儘管標準中的表述不正確,但每個對象都不需要具有唯一的內存地址。一個對象可以在其開始時與子對象具有相同的地址,和/或基類子對象。應該說每個_complete_對象都必須有一個唯一的地址。 – davmac 2016-06-04 21:49:54

2

C++標準1.8.5狀態: -

除非它是一個位字段(9.6),一個最 派生對象應具有非零 尺寸,並應占據一個或多個 存儲字節。 基類 子對象可能具有零大小。一個可以複製的對象 或 標準佈局類型(3。9) 佔用連續的存儲字節。

因此,該標準允許沒有數據成員(並且沒有虛擬)的基類與另一個具有不同類型的子對象共享相同的地址。您可以玩像空基類大小...

struct a{}; 
struct a1{}; 
struct b : public a, public a1{char c;}; 


int main() 
{ 
    std::cout << sizeof(b) << "\n"; 
    std::cout << sizeof(b::a); 
} 


Which outputs (ignoring padding)... 

1 
1 


now try: 


struct a{}; 
struct b : public a {a ax;}; 


int main() 
{ 
    std::cout << sizeof(b) << "\n"; 
    std::cout << sizeof(b::a); 
} 


and the output is ... 

2 
1 

因爲一個(作爲基礎和作爲成員)的兩個實例必須有不同的地址。

BTW:「b :: a」是另一種說「a」的方式。範圍訪問操作符的存在不會請求「類型a的b的基類子對象」。 5.3.3/2說: - 將sizeof應用於基類子對象時,結果爲該對象的類型爲的大小。