要在class之中定義常數會遇到如何初始化該常數的問題。
C++11以前的標準不允許在class definition中初始化members。
嘗試直接在class definition中初始化變數
|
|
compile以上snippet會產生錯誤:
|
|
編譯器說無法初始化non-static data member,還說該功能只能在c++11(以上?)使用
non-static不行 那麼 static呢?
將第五行改成這樣:
|
|
嘗試編譯:
|
|
第二個錯誤訊息是語意錯誤(Semantic error)與C++標準有關。
第一個錯誤訊息則是說:”非intergal型別的class內靜態data member初始化是無效的”
看來std::string是非integral type所以雖然是static type仍然無法在class內初始化
甚麼是Integral type?
在Quora上的問題 What are integra types in C++?
從答案可以很清楚地了解到Integral types是C++型別系統中對於型別的分類,屬於這類的Type有:
type | |
---|---|
布林變數 | bool |
字元 | |
窄字元 | char |
寬字元 | char16_t |
char32_t | |
wchar_t | |
整數 | int |
(包含使用unsigned或short/long keyword衍生的型別)
從上面列出的type觀察可以發現這些type大多很簡單,能用整數表示,以幾個byte的形式存於記憶體。floating point,當作反例,則是需要特別的encoding(如:IEEE-754)才能表示所存數值的type。
再怎麼查,得到的答案都是一個list告訴你那些type是Integral type。可是為需要區分是否為Integral type呢?WHY?
我猜可能跟ISA或compiler實作有關吧?
* ISA => Instruction Set Architecture
常數被指定後就不能被變更,編譯器可以很安心的把它當作macro般在程式碼中展開、inline。在編譯階段任何參照到常數的地方直接抽換成該常數的值。如此一來,常數在runtime可以說是不存在的,因為他的值已經inline到text section而非可以被讀取的變數,也就代表常數沒有address。
char或bool是以整數的形態存於記憶體中,C++裡面可以對char或bool可以像對待數字一樣進行加減乘除。或許被歸類在Integral type的資料可以直接給ALU做運算並依照其type再進行解釋(例如:非0整數作為bool為true)。ISA提供了運算operation,因此編譯器可以很放心的直接把bytes交給機器處理。
floating point或其他user data type可能就無法直接交給ISA處理了,因此沒辦法直接inline,在runtime需要有一塊記憶體儲存其data member也需要address以供參照。
char 是integral type那麼char[]是嗎?
Nope!
|
|
恩…我想也是。array本身代表連續的記憶體空間要怎麼inline呢?
Any other way?
C++之父 stroustrup的網站 提供了原因及方法。
原因
引自stroustrup 的網站
A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.
通常class在header檔中定義,而header檔通常被許多翻譯單元include。為了避免複雜的linker發則C++要求每個物件都要有單一獨特的定義。若C++允許class內定義一個需要被儲存在記憶體的實體,那會打破此規則。
如果一個class的data member是另一個物件那麼可以說該class的定義相依於另一個物件,因為prototype definition主要給compiler決定要分配多少記憶體空間。因此要計算出此class所需的空間還得計算出其data member的空間。如果該data member也相依於其他物件,甚至是目前正在定義的物件…linker勢必要時做複雜的規則避免loop。
解決方法
要如何定義非intergal的data member?
解決方法即是不要使用in-class initialization。把初始化expression移到constructor或是class definition之外。
非static常數(將初始化移置constructor)
在constructor初始化常數。
跟java類似與許final/const留到constructor時才初始化,然而要在C++的constructor初始化常數必須透過initializer-list的方式。
static常數(使用out-of-class definition)
|
|