1. 程式人生 > >【譯】C++ POD的理解(1):aggregates

【譯】C++ POD的理解(1):aggregates

virt 解釋 特殊性 als ack pub tor 不同 虛函數表

在閱讀《深入理解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.i110y.x.i220y.i[0]20y.i[1]30y.f則被值初始化,即被初始化為0.0protectedstatic類型的成員變量沒有被初始化。

Aggregate聯合體則不同,只有第一個成員變量可以用大括號進行初始化。我認為,如果您在c++方面高級到可以考慮使用union(它們的使用可能非常危險,必須仔細考慮)的地步,那麽您可以自己在標準中查找union的規則:-)。

現在我們已經知道了aggregates類型的特殊之處,現在我們嘗試理解一下它對類型的限制,也就是說為什麽會有這些限制。我們應該理解,帶大括號的成員初始化意味著類只不過是其成員變量的集合。如果用戶定義了構造函數,這意味著徐虎需要在初始化成員變量時需要做額外的工作,所以使用大括號初始化會出錯。如果存在虛函數,則意味著該類的對象有一個叫做虛函數表的指針(在大多數實現當中),該指針會在構造函數中進行設置,因此使用大括號初始化是不夠的。作為練習,你可以用這種方法理解其他條件限制的含義:-)。

未完待續。。。

【譯】C++ POD的理解(1):aggregates