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

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