靜態成員變量必須要在類外初始化
type class::name = value;初始化的時候不用帶static但需要有類型,PRotected,privated,public都可以被這樣初始化
靜態成員變量的調用
//通過類類訪問 static 成員變量Student::m_total = 10;//通過對象來訪問 static 成員變量Student stu("小明", 15, 92.5f);stu.m_total = 20;//通過對象指針來訪問 static 成員變量Student *pstu = new Student("李華", 16, 96);pstu -> m_total = 20;static 成員變量不占用對象的內存,而是在所有對象之外開辟內存,即使不創建對象也可以訪問。一個類中可以有一個或多個靜態成員變量,所有的對象都共享這些靜態成員變量,都可以引用它。static 成員變量和普通 static 變量一樣,都在內存分區中的全局數據區分配內存,到程序結束時才釋放。這就意味著,static 成員變量不隨對象的創建而分配內存,也不隨對象的銷毀而釋放內存。而普通成員變量在對象創建時分配內存,在對象銷毀時釋放內存。靜態成員變量初始化時可以賦初值,也可以不賦值。如果不賦值,那么會被默認初始化為 0。全局數據區的變量都有默認的初始值 0,而動態數據區(堆區、棧區)變量的默認值是不確定的,一般認為是垃圾值。靜態成員變量既可以通過對象名訪問,也可以通過類名訪問,但要遵循 private、protected 和 public 關鍵字的訪問權限限制。當通過對象名訪問時,對于不同的對象,訪問的是同一份內存。靜態成員變量可以成為派生類和基類共同使用的數值,也可以成為成員函數的可選參數。靜態成員變量可以是所屬類的類型,普通數據成員只能成為該類的指針或引用。靜態成員函數的地址可以用普通函數指針調用
class A{public: static fun(){}; int fun1(){};}int (*pf1)()=&base::fun;int (base::*pf2)()=&case::fun1;靜態成員函數不可以調用類的非靜態成員,因為這個靜態成員函數并不帶有this指針。靜態成員函數不可以同時聲明為 virtual const volatile函數。靜態成員函數不需要對象名即可調用。非靜態成員函數可以自由調用靜態成員函數和靜態成員變量。調用時機
類作為一個參數整體傳入一個函數的時候,需要調用這個類的拷貝構造函數,進行形參和實參的復制類作為一個結果返回的時候,先產生一個臨時變量,調用拷貝構造函數將返回值拷貝到臨時變量,析構返回的變量,再析構臨時變量需要通過另一個變量初始化的時候
class mode{...}mode A(10);mode B = A;拷貝構造函數分為淺拷貝和深拷貝。默認拷貝構造函數是淺拷貝的一種
默認拷貝構造函數無法處理靜態成員變量只是簡單復制需要自己寫淺拷貝構造函數進行靜態成員變量的復制如果被拷貝對象中包含指針,進行逐位拷貝后新舊兩個指針將指向同一個空間,并且將被重復釋放深拷貝用于需要動態創建新空間時
Rect(const Rect& r) { width = r.width; height = r.height; p = new int; // 為新對象重新動態分配空間 *p = *(r.p); } 可以創建一個private的拷貝構造函數聲明來解決默認值拷貝。???空初始化:即無參數無括號形式
如int i,new int,new int[10].當在所有函數之外時,初始化為0;當在某一函數中時,沒初始化。值初始化:即無參數有括號形式,且括號只能在類型名后,而不能在變量名之后,即只能創無名對象,對象被值初始化為0.
如:int() //創建了一個無名對象,其被值初始化為0.一般將該無名對象初始化化或賦值給某有名對象,或直接作為無名對象使用顯式初始化:即有參數有括號形式,且當為有名對象時括號在對象名之后,為無名對象時括號在類類型名之后。
如:int i(5); new int(5);以下四種必須使用初始化列表: 初始化一個引用成員變量初始化一個const變量當我們在初始化一個子類對象的時候,而這個子類對象的父類有一個顯示的帶有參數的構造函數當調用一個類類型成員的構造函數,而它擁有一組參數的時候析構函數通常使用默認析構函數,但是在之前進行空間改變(指針移位等)的時候一定要自己寫析構函數。析構數組或類組:
class A{ A(){m_a=new int[10];} ~A(){delete [] m_a;} int * m_a;}強制類型轉換支持但并不推薦,推薦使用以下較溫和的方法:
pd = static_cast<double*>(pv);初始化列表不管怎么寫,初始化的順序也只是按照原類內聲明的順序進行。兩種重載方式的比較:
一般情況下,單目運算符最好重載為類的成員函數;雙目運算符則最好重載為類的友元函數。 以下一些雙目運算符不能重載為類的友元函數:=、()、[]、->。
類型轉換函數只能定義為一個類的成員函數而不能定義為類的友元函數。 C++提供4個類型轉換函數:reinterpret_cast(在編譯期間實現轉換)、const_cast(在編譯期間實現轉換)、stactic_cast(在編譯期間實現轉換)、dynamic_cast(在運行期間實現轉換,并可以返回轉換成功與否的標志)。
若一個運算符的操作需要修改對象的狀態,選擇重載為成員函數較好。
若運算符所需的操作數(尤其是第一個操作數)希望有隱式類型轉換,則只能選用友元函數。 當運算符函數是一個成員函數時,最左邊的操作數(或者只有最左邊的操作數)必須是運算符類的一個類對象(或者是對該類對象的引用)。如果左邊的操作數必須是一個不同類的對象,或者是一個內部 類型的對象,該運算符函數必須作為一個友元函數來實現。
當需要重載運算符具有可交換性時,選擇重載為友元函數。
注意事項:
除了類屬關系運算符”.“、成員指針運算符”.*“、作用域運算符”::“、sizeof運算符和三目運算符”?:“以外,C++中的所有運算符都可以重載。
重載運算符限制在C++語言中已有的運算符范圍內的允許重載的運算符之中,不能創建新的運算符。 運算符重載實質上是函數重載,因此編譯程序對運算符重載的選擇,遵循函數重載的選擇原則。
重載之后的運算符不能改變運算符的優先級和結合性,也不能改變運算符操作數的個數及語法結構。
運算符重載不能改變該運算符用于內部類型對象的含義。它只能和用戶自定義類型的對象一起使用,或者用于用戶自定義類型的對象和內部類型的對象混合使用時。
運算符重載是針對新類型數據的實際需要對原有運算符進行的適當的改造,重載的功能應當與原有功能相類似,避免沒有目的地使用重載運算符。
繼承
class Human{ …};class Man : public Human{ …};class Boy : public Man{ …};組合
class Eye{ public: void Look(void);};class Nose{ public: void Smell(void);};class Mouth{ public: void Eat(void);};class Ear{ public: void Listen(void);};class Head{ public: void Look(void) { m_eye.Look(); } void Smell(void) { m_nose.Smell(); } void Eat(void) { m_mouth.Eat(); } void Listen(void) { m_ear.Listen(); } private: Eye m_eye; Nose m_nose; Mouth m_mouth; Ear m_ear;};繼承的關系不同對這個派生類并無影響,而是對該派生類的派生類產生影響。例如private Base(10),則對于該派生類的派生類來說,Base不可見。純虛函數:只聲明,無定義,包含純虛函數的類稱為抽象類,無實際作用,只作為基類。
class <類名>{virtual <類型><函數名>(<參數表>)=0;…};重載和覆蓋的區別
重載的幾個函數必須在同一個類中; 覆蓋的函數必須在有繼承關系的不同的類中覆蓋的幾個函數必須函數名、參數、返回值都相同; 重載的函數必須函數名相同,參數不同。覆蓋的函數前必須加關鍵字Virtual; 重載和Virtual沒有任何瓜葛,加不加都不影響重載的運作。關于C++的隱藏規則:
如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual 關鍵字,基類的函數將被隱藏(注意別與重載混淆)。如果派生類的函數與基類的函數同名,并且參數也相同,但是基類函數沒有virtual 關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。重寫 重載 重定義
重寫(override): 父類與子類之間的多態性。子類重新定義父類中有相同名稱和參數的虛函數。
1) 被重寫的函數不能是 static 的。必須是 virtual 的 ( 即函數在最原始的基類中被聲明為 virtual ) 。
2) 重寫函數必須有相同的類型,名稱和參數列表 (即相同的函數原型)
3) 重寫函數的訪問修飾符可以不同。盡管 virtual 是 private 的,派生類中重寫改寫為 public,protected 也是可以的
重載 (overload): 指函數名相同,但是它的參數表列個數或順序,類型不同。但是不能靠返回類型來判斷。
重定義 (redefining): 子類重新定義父類中有相同名稱的非虛函數 ( 參數列表可以不同 ) 。
重寫與重載的區別 (override) PK (overload)
方法的重寫是子類和父類之間的關系,是垂直關系;方法的重載是同一個類中方法之間的關 系,是水平關系。
重寫要求參數列表相同;重載要求參數列表不同。
重寫關系中,調用那個方法體,是根據對象的類型(對象對應存儲空間類型)來決定;重載關系,是根據調用時的實參表與形參表來選擇方法體的。
對象的動態類型:目前所指對象的類型。是在運行期決定的。對象的動態類型可以更改,但是靜態類型無法更改。
D* pD = new D();//pD的靜態類型是它聲明的類型D*,動態類型也是D* B* pB = pD;//pB的靜態類型是它聲明的類型B*,動態類型是pB所指向的對象pD的類型D* C* pC = new C(); pB = pC;//pB的動態類型是可以更改的,現在它的動態類型是C* 靜態綁定:綁定的是對象的靜態類型,某特性(比如函數)依賴于對象的靜態類型,發生在編譯期。動態綁定:綁定的是對象的動態類型,某特性(比如函數)依賴于對象的動態類型,發生在運行期。只有虛函數是動態綁定,其余函數都是靜態綁定。動態綁定的函數調用的函數體看實際上的對象類型,靜態綁定的函數調用的函數體看聲明的對象類型。虛函數是動態綁定的,但是為了執行效率,缺省參數是靜態綁定的。對于一個使用了虛函數的基類來說:
Base b = d;//直接賦值(產生切割) b.Test(); Base& b2 = d;//使用引用賦值(不產生切割) b2.Test(); Base* b3 = &d;//使用指針賦值(不產生切割) b3->Test(); //覆蓋方法和子類數據丟失的現象生成切割(slice)模板的一般形式:
Template <class或者也可以用typename T>返回類型 函數名(形參表){//函數定義體 }//template是一個聲明模板的關鍵字,表示聲明一個模板關鍵字class不能省略,如果類型形參多余一個 ,每個形參前都要加class <類型 形參表>可以包含基本數據類型可以包含類類型.template <class T> inline T square(T x) { T result; result = x * x; return result; };http://www.49028c.com/ggjucheng/archive/2011/12/18/2292089.html
explicit可以避免隱式調用構造函數。
新聞熱點
疑難解答