[筆記]static / const成員資料與函式|C++

2021年7月16日 14:05
non-const static成員資料無法在類別內初始化,也無法使用初始化列,那到底該如何初始化?static成員是什麼概念?可以將自己宣告為自己的成員資料嗎?如果可以,該如何實現?如果不行,會發生什麼問題? 這是一篇中山資工所 江明朝老師 物件導向程式設計課程筆記,如內容有錯,歡迎留言交流~ 還是很困惑嗎?文章裡有答案喔~😎 好讀版:
一、靜態 (static)與常數 (const) (一)成員資料 (data member) C++11:允許non-static成員資料在類別中直接初始化 1. const static int m1 = 7; // ok 2. const int m2 = 7; // ok 3. static int m3 = 7; // error: not const 4. int m4 = 7; // ok 那要如何初始化第三個例子 (non-const static 成員資料)呢?non-const static 成員資料無法在類別中初始化,只能在class外初始化,而且不能放在.h檔中,必須放在.cpp。 方法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 inline variable (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檔中宣告 (二)成員函式 static for class,static 成員函式和成員資料,不需要先宣告物件/實例,就可以直接匿名呼叫使用。non-static for instance,必須要先宣告物件/實例,才能使用non-static 成員函式和成員資料。 const 物件只能存取const成員函式,但non-const物件可以存取cons和non-const成員函式,const成員函式不能改變類別的成員資料,但可以存取類別的成員資料 (三)JAVA static物件 C++ const 相似於JAVA final,Java: final 1. 類別:當宣告在類別上時,該類別就無法被繼承! 2. 函數:當一個函數被宣告為final時,則繼承他的子類別無法覆寫 3. 變數:當一個變數被宣告為final時,意思是他是一個常數,是無法被修改的。 在JAVA可以在class中宣告自己的static物件 (objects)/實例 (instances),也就是class中自帶自己的static物件。因此可以在class中宣告: 1. public static final className variableName = new className(argument list); //合法 2. public static className variableName = new className(argument list); // 合法 3. public final className variableName = new className(argument list); // 無限遞迴 4. 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' 因為編譯到這裡時還沒有發現定義,不知道該類或者結構的內部成員,沒有辦法具體的構造一個對象,所以會報錯。 二、相關文章 1. 物件導向程式設計|模擬真實世界的方式|江明朝
江老師物件導向程設,評分、老師、課程簡介。 2. AOOP Homework source code
3. [筆記]109-2高等物件導向程式設計 期中考
4. [筆記]介面與實作、運算子多載、左值右值、參數傳遞、回傳多值|C++
運算子多載如何執行?int a = 10; ++a—;這串程式碼為什麼會出錯?居然跟左值又值有關!左值右值真的是一左一右嗎?參數傳遞中的傳指標和傳參考差在哪邊?C++居然無法回傳多值,那我該怎麼回傳多值? 5. [筆記]陣列與指標|C++
我以為我傳的是陣列,但居然傳的是指標?為什麼我無法回傳陣列?只能回傳指標?所以陣列和指標是什麼關係?指標運算怎麼算?釋放動態記憶體時,指標變數會不會也被釋放? 6. [筆記]類別、特殊函式、內嵌函式、函式物件|C++
程序導向的C語言也能實作物件導向?C語言要如何模擬類別?C struct與C++ struct與C++ class這三者有什麼差別?類別中有哪六個特殊函式?初始化和賦值有什麼差別?內嵌函式和巨集很像?物件居然可以拿來當成函式?為什麼C++宣告無參數物件時,不用加()? 7. [筆記]繼承模式與存取權限|C++
繼承模式和存取權限有什麼關係?private繼承不是讓所有存取權限變成private嗎?為什麼衍生類別依舊可以存取基礎類別?類別開發者和使用者有什麼區分?特殊函式的繼承會和一般函式一樣嗎? 8. [筆記]多型與繼承的關係|C++
多型的出現是為了要解決什麼問題?多型觸發的條件是什麼?為什麼多型要和繼承一起使用?多型能保有繼承的優點嗎?多型比繼承又多了什麼功能?特殊函式也能多型嗎? 9. [筆記]static / const成員資料與函式|C++
non-const static成員資料無法在類別內初始化,也無法使用初始化列,那到底該如何初始化?static成員是什麼概念?可以將自己宣告為自己的成員資料嗎?如果可以,該如何實現?如果不行,會發生什麼問題? 10. [筆記]夥伴函式與類別、不夠朋友問題|C++
夥伴函式和成員函式差在哪裡?為什麼輸入/輸出的多載運算子一定要為夥伴函式?如果A類別把B函式當作夥伴,但B函式的參數中沒有A類別,顯然這個B不把A當夥伴,這會造成main()無法找到B函式。
5
留言 1
文章資訊
85 篇文章294 人追蹤
Logo
每天有 10 則貼文
共 1 則留言