一、類的分類:
類在包含內容上大致可以分為以下兩種形式:
class with pointer members ——string類 在構造這種類的時候,拷貝構造函數,賦值與比較運算符重載,全部都要特殊考慮,很可能出現違背開發者意圖的行為。
class without point members ——complex類
二、三大特殊函數:

三大特殊函數即為:
拷貝構造函數: String(const String &str);
賦值運算符重載:
String& Operator=(const String&str);析構函數:
~String();在class with pointer members里必須存在拷貝構造函數三大函數。如果使用編譯器合成的默認函數的話會出現俗稱的“淺拷貝”和“內存泄漏”。 舉例說明:
String a("Hello"); String b(a);若使用的是一個默認的拷貝構造函數,則會使兩個類中的m_data指針指向同一個”Hello”,那么假設析構函數正常運行,a的lifetime終止的時候,會釋放a->m_data指向的”Hello”內存塊。但是b的lifetime并沒有終止,那么b->m_data會指向一個已經被釋放的內存塊,形成空懸指針,萬一在程序的某處使用b,則會出現內存錯誤。賦值函數同理。 我們都知道一個指針有new必有delete,類在lifetime結束的時候,會將所有的在棧上占用的內存還給計算機,但是class with pointer里指針指向的內存全部來自堆上,如果不主動釋放,那么此內存塊將會一直被占用,在小程序中可以并不會出現什么大的問題,但是如果一個類有足夠多個實例,那么將會將堆中的內存全部占用,造成堆中無內存可用的狀況,就是俗稱的“內存泄漏”。
拷貝構造函數讓我們發現一個很有意思的特性:
inline Stirng::String(const String &str) { m_data = new char[strlen(str.m_data)+1]; strcpy(m_data,str.m_data) } 有同學可能發現了 str和我本身這個類的實例,并不是友元,我為什么可以訪問它的PRivate成員? 因為從同一個類中衍生出的實例互為friend。
在拷貝賦值函數中,有一點非常重要,即是要檢測rhs是否是類本身,如果是,可以節省拷貝的時間和空間。如果不是,不檢查的話,可能產生不確定行為,如圖所示:
三、所謂棧和堆: Stack,是存在某作用域的一塊內存空間,隨著你所使用的函數的調用和結束產生以及返回,在函數本體內聲明的任何變量,所使用的內存塊都取自棧(stack)。
heap,是指操作系統提供的一塊global內存空間,程序可以動態分配從中獲取若干區塊。
具體區別可以參照StackOverFlow 問題:What and where are the stack and heap? 棧中的變量,lifetime與包含它的函數同時存在,在函數聲明周期結束時,它的生命周期同時存在。 stack object和global object的lifetime在作用域結束后依然存在,直到整個程序結束時才會結束。 heap object卻不同,在new開始后,如果作用域結束,指針的lifetime結束,但是其指向的內存卻一直沒有被釋放,一直到程序結束,但是我們已經沒有能力再次通過指向它的指針對其進行delete等其他操作,這就造成了內存泄漏。
分配在heap上的數據,有new必有delete。 接下來看一下new和delete的實現。
void *operator new(size_t); //allocate an objectvoid *operator delete(void *); //free an objectvoid *operator new[](size_t); //allocate an arrayvoid *operator delete[](void *); //free an array c++中new和delete函數是不允許重載的,屬于特殊函數,雖然函數聲明有點不太一樣,但是同學們- - 這個真的是函數。new和delete通常需要成對出現,因為其底層實現的原理有所不同,在此不進行討論,其底層最終都是調用c語言的malloc free來進行內存的分配與釋放,但是在delete時 如果delete一個類的時候,會先調用類的析構函數然后再釋放內存,而free并不會(這不廢話么- - 有free的時候c++還不直到在哪里呢)。new先分配memory,再調用構造函數。 四、動態內存分配所得的array: 
這是侯捷老師在debug模式下截取的一個動態分配所得的array內存塊。一個complex內有2個double數據。其中關于debugger header的部分本人水平所限,理解有限。。。就扔一張圖,不獻丑了。
五、關于static的補充: 類的靜態成員存在于任何對象之外,對象不包含任何與靜態成員有關的數據,類似的,靜態成員函數,也不與任何對象綁定在一起,他們不包含this指針,作為結果,靜態成員函數不能聲明成const的,而且我們也不可以在static函數內使用this指針。我們可以使用作用域運算符直接訪問靜態成員:
double r; r = complex::static_function();成員函數不通過作用域運算符可以直接訪問靜態成員。 定義靜態成員: 我們可以在類的內部或者外部定義靜態成員函數,在類的外部定義靜態成員時,不能重復static關鍵字。 因為靜態數據成員不屬于類的任何一個對象,所以他們不是創建類的對象時被定義的。這意味著他們不是由類的構造函數初始化的。而且一般來說,我們不能在類的內部初始化靜態成員。相反的,必須在類的外部定義和初始化每個靜態成員。
新聞熱點
疑難解答