Classes的兩個(gè)經(jīng)典分類
Class without pointer member(s)
complex
Class with pointer member(s)
string
帶指針的class
String class
它將實(shí)現(xiàn)的功能:
三種構(gòu)造方式:
無(wú)初值的仑濒、有初值的 和 拷貝構(gòu)造梢薪。
兩種操作符重載:
“<<”(輸出到“cout”) & “=”(拷貝賦值)
Big Three 三個(gè)特殊函數(shù)
四個(gè)主要函數(shù):
1.默認(rèn)用指針構(gòu)造也糊;
2.拷貝構(gòu)造瓶盛,接受的是自身的類型的對(duì)象址貌;
3.拷貝賦值,只有類帶有指針毁菱,一定要包含有這個(gè)函數(shù)瑰谜;
4.析構(gòu)函數(shù),當(dāng)以這個(gè)類創(chuàng)建的對(duì)象侈离,即將死亡的時(shí)候试幽,它就會(huì)被調(diào)用。
其中2卦碾,3铺坞,4 被稱為 Big Three .
ctor 和 dtor (構(gòu)造函數(shù) 和 析構(gòu)函數(shù))
構(gòu)造函數(shù) ctor :
字符串會(huì)以頭指針的形式傳入,并以‘\0’為結(jié)尾洲胖;因此構(gòu)造函數(shù)要適應(yīng)不同長(zhǎng)度的济榨,所以用‘new’分配一塊大小適合內(nèi)存。
析構(gòu)函數(shù) dtor:
用于清理绿映,清理動(dòng)態(tài)分配的內(nèi)存擒滑,不然就是內(nèi)存泄漏了,用 delete[] 關(guān)鍵字釋放動(dòng)態(tài)分配的內(nèi)存叉弦;
在調(diào)用的示例中:
指針p指向動(dòng)態(tài)分配的內(nèi)存丐一,所以用 delete 釋放內(nèi)存;
當(dāng)離開作用域的時(shí)候淹冰,會(huì)調(diào)用3次析構(gòu)函數(shù)库车,包括以new動(dòng)態(tài)分配的String對(duì)象的析構(gòu)函數(shù)。
class with pointer members (帶有指針的類)必須有 copy ctor (拷貝構(gòu)造函數(shù)) 和 copy operator= (拷貝賦值)
如果不寫拷貝賦值函數(shù)樱拴,而是直接使用原來(lái)‘=’把a(bǔ)的地址復(fù)制到b里去柠衍,b只會(huì)指向a所指的內(nèi)存洋满,原來(lái)所指的內(nèi)存也將會(huì)丟失(造成內(nèi)存泄漏),這叫淺拷貝珍坊,不是我們想要的結(jié)果牺勾,我們要的是把內(nèi)容拷貝過(guò)來(lái)的深拷貝。
在設(shè)計(jì)中應(yīng)該避免有別名出現(xiàn)垫蛆,即多個(gè)指針指向同一塊內(nèi)存禽最。
copy ctor (拷貝構(gòu)造函數(shù))
copy assignment operator(拷貝賦值函數(shù))
經(jīng)典的寫法
① 釋放原來(lái)的內(nèi)存
② 重新申請(qǐng)一個(gè)足夠大的內(nèi)存
③ 將內(nèi)容拷貝進(jìn)來(lái)
然后,在最前面檢測(cè)自我賦值的情況袱饭;如果出現(xiàn)自我賦值,而又沒有檢測(cè)自我賦值呛占,這樣會(huì)因?yàn)閮?nèi)存被釋放而丟失原來(lái)的內(nèi)容虑乖。
(這里的this是指向調(diào)用者的,而且這個(gè)函數(shù)它還是成員函數(shù)晾虑,所以可以直接更改)
output函數(shù)
operator<< 函數(shù)一定是全局函數(shù)疹味,如果它是成員函數(shù)的話cout就要在右邊,因?yàn)槌蓡T函數(shù)的左邊一定是自身類的指針 *this
所謂stack(棧) 帜篇,所謂heap(堆)
Stack糙捺,是存在于作用域(scope)的一塊內(nèi)存空間(memory space)。例如當(dāng)你調(diào)用函數(shù)笙隙,函數(shù)本身即會(huì)形成一個(gè)stack用來(lái)旋轉(zhuǎn)它所接收的參數(shù)洪灯,以及返回地址。
在函數(shù)本體(function body)內(nèi)聲明的任何變量竟痰,其所使用的內(nèi)存塊都取自上述stack签钩。
Heap,或調(diào)system heap坏快,是指由操作系統(tǒng)提供的一塊global(全局的)內(nèi)存空間铅檩,程序可動(dòng)態(tài)分配,從中獲得若干塊莽鸿。
c1所占用的空間來(lái)自Stack昧旨;當(dāng)離開作用域時(shí),它的生命自然結(jié)束祥得;
Complex(3)是個(gè)臨時(shí)創(chuàng)建的對(duì)象兔沃,占用的空間是以new動(dòng)態(tài)分配非得,并由heap提供啃沪;它必須需要手動(dòng)delete掉粘拾。
關(guān)于生命期
stack object:
c1便是,其生命在作用域結(jié)束之際結(jié)束创千。又稱為auto object缰雇,因?yàn)樗鼤?huì)被[自動(dòng)]清理入偷。
static local object(靜態(tài)對(duì)象):
c2便是所謂static object,其生命在作用域結(jié)束之后仍然存在械哟,直到整個(gè)程序結(jié)束疏之。
global object(全局對(duì)象):
c3便是所謂global object,它的生命在整個(gè)程序結(jié)束之后才結(jié)束暇咆。也可以把它被視為一種static object锋爪,其作用域是[整個(gè)程序]。
heap objects:
p所指的便是heap object爸业,其生命在它被deleted之際結(jié)束其骄。
在創(chuàng)建時(shí),我們得到的是一個(gè)指針p扯旷,所以我們應(yīng)該delete指針p拯爽,同時(shí)會(huì)調(diào)用被刪除對(duì)象內(nèi)的析構(gòu)函數(shù)。
如果沒把heap對(duì)象 delete掉钧忽,就會(huì)出現(xiàn)內(nèi)存泄漏(memory leak)毯炮,即是程序失去對(duì)內(nèi)存塊的控制(內(nèi)存塊再也找不回來(lái)了)
以上便會(huì)出現(xiàn)內(nèi)存泄漏,因?yàn)楫?dāng)作用域結(jié)束耸黑,p所指的heap object仍然存在桃煎,但指針p的生命卻結(jié)束了,作用域之外再也看不到p(也就沒機(jī)會(huì)delete p)
動(dòng)態(tài)分配所得的內(nèi)存塊(關(guān)于new和delete)
new其實(shí)也是一個(gè)函數(shù)
new調(diào)用時(shí)大刊,先分配memory为迈,再調(diào)用ctor
以上便是編譯器將new轉(zhuǎn)化的三個(gè)動(dòng)作,實(shí)際上調(diào)了malloc()函數(shù)分配內(nèi)存奈揍。
delete:先調(diào)用dtor曲尸,再釋放memory
編譯器轉(zhuǎn)化的兩個(gè)動(dòng)作,它里面也調(diào)用了free()函數(shù)男翰。
動(dòng)態(tài)分配數(shù)組對(duì)象
以上的用法叫做array new和array delete另患;
array new一定要搭配array delete,因?yàn)閐elete只是會(huì)刪除指針?biāo)傅膬?nèi)存蛾绎,數(shù)組內(nèi)的其它內(nèi)存就會(huì)因此而丟失(內(nèi)存泄漏)昆箕。
(注:動(dòng)態(tài)分配的內(nèi)存區(qū)塊中,頭尾也會(huì)有一個(gè)稱為cookie標(biāo)識(shí)的內(nèi)存塊租冠,標(biāo)識(shí)中用16進(jìn)位表示狀態(tài)鹏倘,結(jié)尾為1表示“分配”,并且動(dòng)態(tài)分配的內(nèi)存區(qū)塊一定是16的倍數(shù))
擴(kuò)展補(bǔ)充
關(guān)于static
一般的成員函數(shù)都會(huì)有this point
以上是從編譯器角度看到的成員函數(shù)的調(diào)用顽爹。
如果一個(gè)數(shù)據(jù)在每個(gè)同類的對(duì)象都是相同的纤泵,這時(shí)候它就是應(yīng)該變成static數(shù)據(jù),在它的聲明前加上static便是镜粤;
static數(shù)據(jù)要在class body外定義捏题,上面黃色部分便是玻褪。
static函數(shù):它只能處理static的數(shù)據(jù),也沒this point公荧;它可以通過(guò)object調(diào)用(通過(guò)已經(jīng)創(chuàng)建的對(duì)象調(diào)用)带射,也可以通過(guò)class name調(diào)用(直接修改類的static數(shù)據(jù),作用于所有通過(guò)這個(gè)類創(chuàng)建的對(duì)象)
關(guān)于把ctors放在private區(qū)
之前說(shuō)過(guò)這種把構(gòu)造函數(shù)放在private中的類循狰,叫做Singleton(單體)
上面的便所謂的Singleton窟社,因?yàn)樗荒鼙煌饩硺?gòu)造,所以它須要在自身內(nèi)被構(gòu)造绪钥,并且一定是static的灿里;
因?yàn)閟tatic函數(shù)可以通過(guò)class name調(diào)用(上面的小框便是),通過(guò)getInstance()調(diào)用‘a(chǎn)’程腹,這樣就解決了不能調(diào)用的問題钠四。
但就是因?yàn)閟tatic的數(shù)據(jù)一被定義了就會(huì)直到整個(gè)程序結(jié)束,它的生命才會(huì)結(jié)束跪楞;
用上面的寫法可以解決static object不被使用也一直存在的問題。
關(guān)于cout
cout屬于右上角的class侣灶,而這個(gè)類型繼承自ostream甸祭,所以它也是屬于ostream。
從ostream里可以發(fā)現(xiàn)褥影,它做了好多種operator<<的重載池户,這正是cout可以接受多種類型數(shù)據(jù)的原因。
class template(類模板)
如果在設(shè)計(jì)沒有確定類里面的參數(shù)在使用時(shí)的類型凡怎,可以通過(guò)類模板在使用時(shí)再確定它們的數(shù)據(jù)類型校焦。
寫法如上:在類的前面聲明T是一個(gè)關(guān)鍵字;用T替換參數(shù)類型的位置统倒。
圖中左下角便是它的調(diào)用方法寨典。
function template(函數(shù)模板)
用法:正如上面的比大小函數(shù),為了適用于所有不同類型的對(duì)象房匆,所以它采用了函數(shù)模板耸成,代替未知的對(duì)象類型;它的聲明和類模板相似浴鸿,要在函數(shù)之前用加上黃色的語(yǔ)句井氢。
(使用這種方式時(shí),不要忘了還要對(duì)相應(yīng)的操作符進(jìn)行重載)
namespace
它的作用是將它里面的東西包裝起來(lái)岳链,防止和其它人重名花竞。(std是指標(biāo)準(zhǔn)庫(kù),標(biāo)準(zhǔn)庫(kù)的所有東西都被包在std里)
用法:
最簡(jiǎn)單的用法就是using directive(全開)(寫法如上)掸哑,這樣在下面的所以標(biāo)準(zhǔn)庫(kù)的函數(shù)都不用寫命名了(cin和cout的全名是std::cin和std::cout)约急。
using declaration(逐個(gè)打開‘聲明’)
正如上面的零远,它只聲明了cout,所以就是只有cout不用寫全名烤宙。
更多拓展遍烦,此課程不再詳述
Composition(復(fù)合),表示has-a
表示queue(隊(duì)列躺枕,先進(jìn)先出)包含deque(兩端都可以進(jìn)出的隊(duì)列)服猪;
queue擁有deque的所有功能,這樣便是所謂Composition拐云,而queue自身沒有實(shí)現(xiàn)功能只是改變功能的名字罢猪,這類情況叫做Adapter(改裝);
Adapter:適用于已經(jīng)存在一個(gè)類能實(shí)現(xiàn)所需的功能時(shí)叉瘩,只是有一些情況不同(可能是接口不一樣-函數(shù)名不同)膳帕,它是復(fù)合中一種特殊的情況
Composition類的大小要加上它所包含的所有類的大小
Composition關(guān)系下的構(gòu)造和析構(gòu)
當(dāng)Container包含Component時(shí):
編譯器會(huì)在Container的構(gòu)造函數(shù)名之后,自動(dòng)加上Component的默認(rèn)構(gòu)造(‘ : Component() ’)薇缅,這便使它先要執(zhí)行所含類的構(gòu)造函數(shù)(如果需要調(diào)用所含類的其它構(gòu)造函數(shù)危彩,需要自己寫上)然后才執(zhí)行自己;
編譯器也會(huì)在Container的析構(gòu)函數(shù)中加上所含有類的析構(gòu)函數(shù)泳桦,且是先執(zhí)行自己汤徽。
Delegation (委托) . Composition by reference
這里是通過(guò)指針,指向功能實(shí)現(xiàn)的類灸撰,而自身卻只是一個(gè)對(duì)外的接口谒府,所有功能的實(shí)現(xiàn)都是通過(guò)指針委托‘功能實(shí)現(xiàn)類’完成的,這樣它便有了高度的彈性(可以改變它功能浮毯,而不影響整體完疫,也可以方便地增加它的功能)
這樣的寫法稱為pimpl(private implementation),左邊的是Handle债蓝,右邊的是Body壳鹤,其主要作用是解開類的使用接口和實(shí)現(xiàn)的耦合
(這里用到是指針,但為什么也叫by reference而不是by point惦蚊?因?yàn)闃I(yè)界中沒有by point這個(gè)說(shuō)法器虾,by point也叫by reference)
Inheritance(繼承),表示is-a
(在C++中蹦锋,struct其實(shí)是一種class)
語(yǔ)法:就是加黃色的一行兆沙;上面的是父類,下面的是子類(子類繼承父類)莉掂。
C++的繼承擁有三種方式葛圃,分別是public / private / protected,其中最重要的public。(而Java中只有public)
子class擁有自己的part(成份)同時(shí)库正,還涵蓋了父class的prat曲楚。
圖示,用空心三角形表示繼承褥符。
Inheritance(繼承)關(guān)系下的構(gòu)造和析構(gòu)
與Composition的關(guān)系相似龙誊,但前提是父類中的構(gòu)造函數(shù)一定要是虛函數(shù)(后面會(huì)解釋虛函數(shù)),否則不會(huì)出圖中下面是兩個(gè)動(dòng)作
Inheritance(繼承)with virtual functions(虛函數(shù))
父類的成員函數(shù)分三種類型:非虛函數(shù)喷楣、虛函數(shù)趟大、純虛函數(shù)。
虛函數(shù)的語(yǔ)法:在非虛函數(shù)之前加virtual铣焊。
非虛函數(shù)逊朽,子類不能重新定義,在父類中定義好曲伊,供子類使用叽讳。
虛函數(shù),在父類中定義坟募,子類也可以重新定義它岛蚤。
純虛函數(shù),在父類中聲明懈糯,在子類中定義灭美。
Template Method(模板方法):二十三種設(shè)計(jì)模式之一。
示例中昂利,通過(guò)子類的對(duì)象調(diào)用父類的函數(shù),當(dāng)調(diào)用虛函數(shù)時(shí)铁坎,編譯器會(huì)檢查子類是否有重新定義蜂奸。
Inheritance + Composition關(guān)系下的構(gòu)造和析構(gòu)
在第一種情況中,Base part和Component part它兩的構(gòu)造的先后是順序硬萍,而析構(gòu)則是逆序的扩所。
在第二種情況中,與之前的相類似朴乖,不多說(shuō)祖屏。
Delegation(委托) + Inheritance(繼承)
左邊委托右邊,右邊作為父類买羞,可以被繼承袁勺。
這樣的寫法可以使左邊被創(chuàng)建,內(nèi)容可以被多個(gè)繼承右邊的子類觀察畜普。(就像一個(gè)文件在同一個(gè)軟件里被打開期丰,但有不同的查看窗口)