non-const static成員資料無法在類別內初始化,也無法使用初始化列,那到底該如何初始化?static成員是什麼概念?可以將自己宣告為自己的成員資料嗎?如果可以,該如何實現?如果不行,會發生什麼問題?
還是很困惑嗎?文章裡有答案喔~😎
一、靜態 (static)與常數 (const)
(一)成員資料 (data member)
C++11:允許non-static成員資料在類別中直接初始化
- const static int m1 = 7; // ok
- const int m2 = 7; // ok
- static int m3 = 7; // error: not const
- int m4 = 7; // ok
方法1:在.cpp宣告
宣告的時候,class外的變數不能加上static且前面要加上領域,如果不給值,預設就是0。
一般而言,鏈結器會合併 C++ 樣版產生的函式或變數,所以我們可以利用這個特性宣告靜態資料成員,然後以 vector<void*>::count存取該變數:
template<>
class vector<void*>{
public:
...
...
private:
std::vector<void*> vec;
static int count; // 在.h檔宣告
};
int vector<void*>::count = 0; // 要在.cpp檔宣告
方法2:使用內嵌變數 C++17
動機:因為編譯 C++ 函式庫通常必需處理很瑣碎的細節,所以一些函式庫作者傾向將整個實作都放在標頭檔,使用者只要引用標頭檔就能直接使用函式庫。這類函式庫我們通常稱為 Header-Only Library(標頭檔函式庫)。然而編寫 Header-Only Library 並不是一件簡單的事。
Inline Variable(內嵌變數)是以 inline 關鍵字修飾的變數(包含全域變數或靜態資料成員);以 constexpr 關鍵字修飾的靜態資料成員也是 Inline Variable。
template<>
class vector<void*>{
public:
...
...
private:
std::vector<void*> vec;
inline static int count = 0; // Method 1
inline static int count {0}; // Method 2
static int count; // Method 3.1
};
inline int vector<void*>::count = 0; // Method 3.2 在.h檔中宣告
(二)成員函式
const 物件只能存取const成員函式,但non-const物件可以存取cons和non-const成員函式,const成員函式不能改變類別的成員資料,但可以存取類別的成員資料
(三)JAVA static物件
C++ const 相似於JAVA final,Java: final
- 類別:當宣告在類別上時,該類別就無法被繼承!
- 函數:當一個函數被宣告為final時,則繼承他的子類別無法覆寫
- 變數:當一個變數被宣告為final時,意思是他是一個常數,是無法被修改的。
在JAVA可以在class中宣告自己的static物件 (objects)/實例 (instances),也就是class中自帶自己的static物件。因此可以在class中宣告:
- public static final className variableName = new className(argument list); //合法
- public static className variableName = new className(argument list); // 合法
- public final className variableName = new className(argument list); // 無限遞迴
- public className variableName = new className(argument list); // 無限遞迴
重點在於static這個保留字,static可避免落入無限遞迴的陷阱中。因為宣告static的變數,在實例化的過程中,static變數早就已經初始化過了,故不會去再去初始化這個變數,因此就不會落入無限遞迴。
static變數的存取,不用真的實例化一個物件,即可直接匿名存取static變數,因為static變數早就已經初始化過了,故可以直接存取其值,例如:className.staticVar。而非static變數的存取,一定要先實例化後,才能存取此變數,因為尚未實例化也代表non-static variable尚未初始化,故沒有值也當然無法存取。
舉個例子,有個類別叫水果,水果類別有顏色和大小這兩個資料。如果顏色是static變數,且在定義時就給定此顏色為紅色。則我們在實例化時,就只需要提供大小去實例化紅色水果。我們可以實例化出紅色大芭樂、紅色中蘋果、紅色小葡萄,因此即使這三個實例尚未被實例化,我們也可以知道此水果類別的顏色是紅色。
而non-static大小就必須要實例化後,才能得知,因爲non-static大小不是所有物件共享的資料;而static 顏色是所有物件都共享的資料,也就是紅色是所有紅色水果共享的特徵。因此如果將水果類別的static 顏色改成黃色,所有的實例顏色全部皆為黃色,即黃色大芭樂、黃色中蘋果、黃色小葡萄。
因此static資料成員是被所有物件所共享的特徵,且此特徵在實例化前,就已經被初始化給值,故在實例化時,就不會理static資料成員,故不會落入無限遞迴。
即使JAVA能夠在類別內宣告自己的內別物件,但C++仍無法:
C++編譯器無法區分這是一個成員函數還是一個成員變數!
static const Type ch("char", static_cast<int>(Tag::_BASIC), 1); // 在Type類別內宣告一個Type物件1
Expected parameter declarator
但在C++中依舊會出錯!因為編譯器會認定這是一個不完整的結構!
static const Type ch = Type("char", static_cast<int>(Tag::_BASIC), 1); // 在Type類別內宣告一個Type物件2
Invalid use of incomplete type 'Type'
因為編譯到這裡時還沒有發現定義,不知道該類或者結構的內部成員,沒有辦法具體的構造一個對象,所以會報錯。
留言
張貼留言