【譯】C++ POD的理解(1):aggregates
在閱讀《深入理解C++11》時對POD的理解有些疑惑,stack overflow上有一篇高分回答寫得非常棒,現在我把它翻譯一遍加深一下自己的理解(原文):
如何閱讀這篇文章
這篇文章有點長,如果你想同時了解aggregates和PODs(Plain Old Date),就請花點時間把這篇文章讀完。如果你僅僅對aggregates感興趣,則只需閱讀第一部分就好。如果你只對PODs感興趣,你必須首先搞清楚aggregates的定義、含義和示例,然後你就可以跳到PODs部分閱讀,但我依然建議還是把第一部分閱讀完畢。Aggregates的概念對於定義PODs來說是必不可少的。
什麽是aggregates以及它的特殊性
在C++標準中的正式定義(C++03 8.5.1 §1):
Aggregate是一個數組或者是一個沒有用戶定義的構造函數、沒有private和protected的非靜態成員變量、沒有基類和虛函數的類。
讓我們來解析一下這個定義。首先,任何數組都是一個aggregate類型。一個類如果滿足以下條件也可以是aggregate類型。。。等待,我們好像忘記說結構體和聯合體,它們可以成為aggregate類型嗎?答案是他們也可以成為aggregate類型。在C++中,class術語指的是所有的類、結構體和聯合體。所以說,一個類(或者結構體,或者聯合體)滿足上述定義的條件時,就是aggregate類型的。這些條件意味著什麽?
- 這並不意味著aggregate類不能擁有構造函數,事實上它可以擁有默認的構造函數和/或賦值構造函數,只要它們是編譯器隱式聲明,而不是用戶顯示聲明;
- 沒有private和protected的非靜態成員變量,你可以定義很多private和protected的成員函數(構造函數除外)和private和protected的靜態成員變量,這都不違背aggregate的規則;
- aggregate類可以擁有用戶聲明/用戶定義的賦值操作和/或析構函數;
- 數組都是aggregate類型,即便數組元素是非aggregate類型。
現在讓我們來看一些例子:
class NotAggregate1 { virtual void f() {} //remember? no virtual functions }; class NotAggregate2 { int x; //x is private by default and non-static }; class NotAggregate3 { public: NotAggregate3(int) {} //oops, user-defined constructor }; class Aggregate1 { public: NotAggregate1 member1; //ok, public member Aggregate1& operator=(Aggregate1 const & rhs) {/* */} //ok, copy-assignment private: void f() {} // ok, just a private function };
你已經明白了aggregates的含義。現在讓我們來看下aggregates有什麽特別之處。與非aggregate類不同的是,aggregates類型可以用{}進行初始化。這種初始化語法通常被用於數組,但我們現在了解到的是aggregates。所以讓我們先從數組開始:
Type array_name[n] = {a1, a2, …, am};
如果 m==n
第i個元素用ai初始化;
如果 m<n
前m個元素分別用a1、a2、a3... am;剩余n-m個元素如果可能的話會用值初始化(後面會解釋這個名詞);
如果 m>n
編譯器將報出一個錯誤;
其余的情況如a[] = {1,2,3};
會假設數組(n)的大小為m,因此int a[] = {1,2,3}; 等同於a[3] = {1,2,3};
當一個對象是標量類型(bool, int, char, double, 指針等)時,它的值初始化指得是用0進行初始化(bool類型值為false,double類型值為0等)。
數組初始化的例子:
class A
{
public:
A(int) {} //no default constructor
};
class B
{
public:
B() {} //default constructor available
};
int main()
{
A a1[3] = {A(2), A(1), A(14)}; //OK,n == m
A a2[3] = {A(2)}; //ERROR,A沒有默認構造函數,不可以用值初始化a2[1]和a2[2]
B b1[3] = {B()}; //OK, b1[1] and b1[2]用值初始化,在這個例子中用的默認構造函數
int Array1[1000] = {0}; //所有的元素都被初始化為0
int Array2[1000] = {1}; //只有第1個元素初始化為1,其余的元素都初始化為0
bool Array3[1000] = {}; //大括號可以為空,所有元素初始化為false
int Array4[1000]; //沒有初始化,這和空初始化{}不同
//在這個例子中元素沒有值初始化,擁有不確定的值
//(除非Array4是一個全局數組)
int array[2] = {1, 2, 3, 4}; //ERROR,太多元素被初始化
}
現在讓我們看下如何使用打括號初始化aggregates類。與上述大致相同。我們將按照非靜態數據成員在類定義中的出現順序初始化它們,而不是數組元素(根據定義,它們都是公共的)。如果需要初始化的比成員變量少,那麽其他元素都是進行值初始化。如果無法對未進行顯式初始化的某個成員變量進行值初始化,則在編譯時會遇到錯誤。如果初始化成員變量的數量超過所需的數量,我們還是會在編譯的時候遇到錯誤。
struct X
{
int i1;
int i2;
};
struct Y
{
char c;
X x;
int i[2];
float f;
protected:
static double d;
private:
void g(){}
};
Y y = {'a', {10, 20}, {20, 30}};
在上述例子中y.c
被初始化為‘a‘
,y.x.i1
為10
,y.x.i2
為20
,y.i[0]
為20
,y.i[1]
為30
,y.f
則被值初始化,即被初始化為0.0
。protected的static類型的成員變量沒有被初始化。
Aggregate聯合體則不同,只有第一個成員變量可以用大括號進行初始化。我認為,如果您在c++方面高級到可以考慮使用union(它們的使用可能非常危險,必須仔細考慮)的地步,那麽您可以自己在標準中查找union的規則:-)。
現在我們已經知道了aggregates類型的特殊之處,現在我們嘗試理解一下它對類型的限制,也就是說為什麽會有這些限制。我們應該理解,帶大括號的成員初始化意味著類只不過是其成員變量的集合。如果用戶定義了構造函數,這意味著徐虎需要在初始化成員變量時需要做額外的工作,所以使用大括號初始化會出錯。如果存在虛函數,則意味著該類的對象有一個叫做虛函數表的指針(在大多數實現當中),該指針會在構造函數中進行設置,因此使用大括號初始化是不夠的。作為練習,你可以用這種方法理解其他條件限制的含義:-)。
未完待續。。。
【譯】C++ POD的理解(1):aggregates