程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Aggregate類型以及值初始化,aggregate初始化

Aggregate類型以及值初始化,aggregate初始化

編輯:C++入門知識

Aggregate類型以及值初始化,aggregate初始化


引言

在C++中,POD是一個極其重要的概念。要理解POD類型,我們首先需要理解Aggregate類型。下文根據stackoverflow上的回答將對Aggregate類型做一個全面的解讀。

對於Aggragates的定義

C++標准(C++ 03 8.5.1 §1)中的正式定義如下:

 

An aggregate is an array or a class (clause 9) with no user-declared constructors (12.1), no private or protected non-static data members (clause 11), no base classes (clause 10), and no virtual functions (10.3).

譯文:一個Aggregate是一個數組或者一個沒有用戶聲明構造函數,沒有私有或保護類型的非靜態數據成員,沒有父類和虛函數的類型

從定義中我們首先可以解讀出以下兩點信息:

1. 數組一定是Aggregate。

2. 滿足以下定義的class也可以是Aggregate(注意:class包括classes、structs和unions):

-> Aggregate不能擁有用戶自定義的構造函數。事實上,它可以擁有一個默認構造函數或者一個默認復制構造函數,只要它們是被編譯器聲明,而非用戶自定義的即可。

-> Aggregate不能擁有private或者protected的非靜態數據成員。事實上,你可以定義其他private和protected成員方法(不包括構造函數,對於Aggregate,不能由用戶自定義構造函數,參見上條),也可以定義private和protected靜態類型的數據成員和方法,這都不會違背aggregate類型的規則。

-> 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
};

Why Aggregates are special?

與非Aggregate類型不同的是,Aggregate類型可以使用“{}”來進行初始化。這種初始化語法,實際上在數組中是很常見的。別忘了我們的數組類型也屬於Aggregate,來看以下例子:

Type array_name[n] = {a1, a2, ..., am};

1. 如果m與n相等:很自然,數組的第i個元素被初始化為ai。

2. 如果m小於n   :數組的前m個元素被依次初始化為a1,a2,…,am,剩余的n-m個元素將進行值初始化(前提是可以進行值初始化)。

3. 如果m大於n   :產生編譯錯誤。

注意,類似於以下形式也是正確的:

int a[] = {1,2,3};) // 等價於 int a[3] = {1,2,3};

數組的長度將由編譯器推測為3,因此以上兩種形式是等價的。

值初始化(value-initialization

先來看以下解釋,摘自stackoverflow:

When an object of scalar type (bool, int, char, double, pointers, etc.) is value-initialized it means it is initialized with 0 for that type (false for bool, 0.0 for double, etc.). When an object of class type with a user-declared default constructor is value-initialized its default constructor is called. If the default constructor is implicitly defined then all nonstatic members are recursively value-initialized. This definition is imprecise and a bit incorrect but it should give you the basic idea. A reference cannot be value-initialized. Value-initialization for a non-aggregate class can fail if, for example, the class has no appropriate default constructor.

譯文如下:

對於標量類型(如:bool、int、char、double、指針)的對象如果按值初始化,是指其將被初始化為0(如:bool類型將被初始化為false,double類型將被初始化為0.0)。

默認構造函數由用戶自定義的類類型對象如果按值初始化,那麼其默認構造函數將會被調用;如果默認構造函數為隱式定義,那麼所有的非靜態數據成員將會遞歸地按值初始化。

雖然以上定義並不精確,也不完全,但是可以讓我們有個基本的認識。注意,引用不能按值初始化。對於非Aggregate類型的class進行值初始化,可能會失敗,例如沒有合適的默認構函數。

1. 來看以下數組初始化的例子:

class A()
{
   A(int){} // 用戶自定義了構造函數,因此是非Aggrerate類型。注意,類A沒有默認構造函數。定義對象時,無法進行值初始化
};
class B()
{
   B() {}   // 用戶自定義了構造函數,因此是非Aggrerate類型。注意,類B擁有可用的默認構造函數。定義對象時,可以進行值初始化。 
};

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]和b1[2]使用默認構造函數按值初始化
  
  int Array1[1000] = {0};        // 所有元素被初始化為0
  int Array2[1000] = {1};        // 注意: 只有第一個元素被初始化為1,其他為0;
  bool Array3[1000] = {};        // 大括號裡可以為空,所有元素被初始化為false;
  
  int Array4[1000];              // 沒有被初始化. 這和空{}初始化不同;這種情形下的元素沒有按值初始化,他們的值是未知的,不確定的; (除非Array4是全局數據)
  
  int array[2] = {1,2,3,4};      // ERROR, 太多初始值,編譯出錯。
}

2. 現在我們來看Aggregates類類型是如何使用{ }進行初始化的。和對數組進行初始化非常類似,按照在類內部聲明的順序(按照定義都必須是public類型)初始化非靜態類型的成員變量。如果初始值比成員少,那麼其他的成員將按值初始化。如果有一個成員無法進行按值初始化,我們將會得到一個編譯期錯誤。如果初始值比成員多,我們同樣得到一個編譯期錯誤。

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數據成員d不會被初始化,因為它是靜態類型的。 

總結

我們知道了Aggregates的特別之處,現在讓我們來嘗試理解一下它對類型的限制,也就是說為什麼會有這些限制。來看以下解釋,同樣摘自stackoverflow:

We should understand that memberwise initialization with braces implies that the class is nothing more than the sum of its members. If a user-defined constructor is present, it means that the user needs to do some extra work to initialize the members therefore brace initialization would be incorrect. If virtual functions are present, it means that the objects of this class have (on most implementations) a pointer to the so-called vtable of the class, which is set in the constructor, so brace-initialization would be insufficient. You could figure out the rest of the restrictions in a similar manner as an exercise :)

我們應當理解使用{ }對成員進行逐一初始化意味著這一類型僅僅是成員的集合。

如果有一個用戶定義的構造函數,那意味著用戶需要做一些額外的工作來初始化成員,因此使用{ }初始化是不正確的。如果出現了虛函數,那意味著這個類型(大多數實現)有一個指向vtable的指針,需要在構造函數內設置,所以使用{ }初始化是不夠的。我們可以按照這種方式理解其他限制的含義。

下篇博文,我們會通過Aggregates類型來介紹POD(原生數據類型)。

參考文獻

1. What are Aggregates and PODs and how/why are they special?


錯誤:"int"類型的值不可以用於初始化" int * "類型的實體?

提示很明顯,類型不匹配,無法進行隱士轉換而且這種直接給指針賦值的方式也是很不提倡的,如果你要把n[3]的值賦給指針p所指向的地址的話可以這樣做p = &n[3] 或*p = n[3]
 

什叫初始化與初始值?

這要上你說是什麼,比如說在Visual Basic中兩種方式將初始值賦給對象的...這種初始化方式用於類中的常量成員和引用成員的初始化;派生類初始化基類對象和子對象的初始化....類中說明的靜態變量需要在類外進行定義和初始化,否則沒有初始值;如果是靜態的對象,則需要以初始化的方式表示調用構造...

沒有顯式地賦初值,這個全局變量的初始值就是0。 局部變量的作用域只限於聲明所在的塊內,通常是在過程、...常量名稱:過程類型=初始值; 聲明一個過程類型的常量只需給出過程或函數名,給出的過程或函數必須與常量的類型賦值兼容,
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved