C++知識(shí)點(diǎn)總結(jié)(一)

內(nèi)容比較長(zhǎng),可復(fù)制出來(lái)進(jìn)行搜索
如有錯(cuò)誤希望大家指出,僅供參考
C與C++的區(qū)別?
一、C++介紹
????本賈尼·斯特勞斯特盧普,與1979年4月份貝爾實(shí)驗(yàn)室的本賈尼博士在分析UNIX系統(tǒng)分布內(nèi)核流量分析時(shí),希望有一種有效的更加模塊化的工具。
????1979年10月完成了預(yù)處理器Cpre,為C增加了類(lèi)機(jī)制,也就是面向?qū)ο螅?983年完成了C++的第一個(gè)版本,C?with?classes也就是C++。
????C++與C的不同點(diǎn):
????1、C++完全兼容C的所有語(yǔ)法(內(nèi)容)
????2、支持面向?qū)ο蟮木幊趟枷?/p>
????3、支持運(yùn)算符重載
????4、支持泛型編程、模板
????5、支持異常處理
????6、類(lèi)型檢查嚴(yán)格
二、第一個(gè)C++程序
????1、文件擴(kuò)展名
????????.cpp?.cc?.C?.cxx
????2、編譯器
????????g++?大多數(shù)系統(tǒng)需要額外安裝,Ubuntu系統(tǒng)下的安裝命令:
????????????sudo?apt-get?update
????????????sudo?apt-get?install?g++
????????gcc也可以繼續(xù)使用,但需要增加參數(shù)?-xC++?-lstdc++
????3、頭文件
????????#include?<iostream>
????????#include?<stdio.h>?可以繼續(xù)使用,但C++建議使用?#include?<cstdio>
????4、輸入/輸出
????????cin?>>?輸入數(shù)據(jù)
????????cout?<<?輸出數(shù)據(jù)
????????cin/cout會(huì)自動(dòng)識(shí)別類(lèi)型
????????scanf/printf可以繼續(xù)使用
????????注意:cout和cin是類(lèi)對(duì)象,而scanf/printf是標(biāo)準(zhǔn)庫(kù)函數(shù)。
????5、增加了名字空間
????????std::cout
????????using?namespace?std;
????????
三、名字空間
????1、什么是名字空間
????在C++中經(jīng)常使用多個(gè)獨(dú)立開(kāi)發(fā)的庫(kù)來(lái)完成項(xiàng)目,由于庫(kù)的作者或開(kāi)發(fā)人員沒(méi)見(jiàn)過(guò)面,因此命名沖突在所難免。
????2、為什么需要名字空間
????在項(xiàng)目中函數(shù)名、全局變量、結(jié)構(gòu)、聯(lián)合、枚舉、類(lèi),非常有可能名字沖突,而名字空間就對(duì)這些命名進(jìn)行邏輯空間劃分(不是物理單元?jiǎng)澐郑?/p>
為了解決命名沖突,C++之父為防止命名沖突給C++設(shè)計(jì)一個(gè)名字空間的機(jī)制。
????通過(guò)使用namespace?XXX把庫(kù)中的變量、函數(shù)、類(lèi)型、結(jié)構(gòu)等包含在名字空間中,形成自己的作用域,避免名字沖突。
????namespace?xxx
????{
????}//?沒(méi)有分號(hào)
????注意:名字空間也是一種標(biāo)識(shí)符,在同一作用域下不能重名。
????3、同名的名字空間有自動(dòng)合并(為了聲明和定義可以分開(kāi)寫(xiě))
????同名的名字空間中如果有重名的依然會(huì)命名沖突
????4、名字空間的使用方法
????::域限定符
????空間名::標(biāo)識(shí)符?//?使用麻煩,但是非常安全
????using?namespace?空間名;?把空間中定義的標(biāo)識(shí)符導(dǎo)入到當(dāng)前代碼中
????????不建議這樣使用,相當(dāng)于把垃圾分類(lèi)后,又導(dǎo)入同一個(gè)垃圾車(chē),依然會(huì)沖突
????5、無(wú)名名字空間
????不屬于任何名字空間中的標(biāo)識(shí)符,隸屬于無(wú)名名字空間。
????無(wú)名名字空間中的成員使用?::標(biāo)識(shí)符?進(jìn)行訪(fǎng)問(wèn)。
????如何訪(fǎng)問(wèn)被屏蔽的全局變量。
????6、名字空間的嵌套
????名字空間內(nèi)部可以再定義名字空間,這種名字空間嵌套
????內(nèi)層的名字空間與外層的名字空間的成員,可以重名,內(nèi)層會(huì)屏蔽外層的同名標(biāo)識(shí)符。
????多層的名字空間在使用時(shí)逐層分解。
????n1::n2::num;
????namespace?n1
????{
????????int?num?=?1;
????????namespace?n2
????????{???
????????????int?num?=?2;
????????????namespace?n3
????????????{
????????????}
????????}
????}
????7、可以給名字空間取別名
????由于名字空間可以嵌套,這樣就會(huì)導(dǎo)致在使用內(nèi)層成員時(shí)過(guò)于麻煩,可以給名字空間取別名來(lái)解決這類(lèi)問(wèn)題。
????namespace?n123?=?n1::n2::n3;
四、C++的結(jié)構(gòu)
????1、不再需要?typedef?,在定義結(jié)構(gòu)變量時(shí),可以省略struct關(guān)鍵字
????2、成員可以是函數(shù)(成員函數(shù)),在成員函數(shù)中可以直接訪(fǎng)問(wèn)成員變量,不需要.或->,但是C的結(jié)構(gòu)成員可以是函數(shù)指針。
????3、有一些隱藏的成員函數(shù)(構(gòu)造、析構(gòu)、拷貝構(gòu)造、賦值構(gòu)造)。
????4、可以繼承,可以設(shè)置成員的訪(fǎng)問(wèn)權(quán)限(面向?qū)ο螅?/p>
五、C++的聯(lián)合
????1、不再需要?typedef?,在定義結(jié)構(gòu)變量時(shí),可以省略u(píng)nion關(guān)鍵字
????2、成員可以是函數(shù)(成員函數(shù)),在成員函數(shù)中可以直接訪(fǎng)問(wèn)成員變量,不需要.或->,但是C的結(jié)構(gòu)成員可以是函數(shù)指針。
????3、有一些隱藏的成員函數(shù)(構(gòu)造、析構(gòu)、拷貝構(gòu)造、賦值構(gòu)造)。
??
六、C++的枚舉
????1、定義、使用方法與C語(yǔ)言基本一致。
????2、類(lèi)型檢查比C語(yǔ)言更嚴(yán)格
七、C++的布爾類(lèi)型
????1、C++具有真的布爾類(lèi)型,bool是C++中的關(guān)鍵字,在C語(yǔ)言中使用布爾類(lèi)型需要導(dǎo)入頭文件stdbool.h(在C11中bool應(yīng)該是數(shù)據(jù)類(lèi)型了)。
????2、在C++中?true?false?是關(guān)鍵字,而在C語(yǔ)言中不是。
????3、在C++中?true?false?是1字節(jié),而C語(yǔ)言中是4字節(jié)。
八、C++的void*
????1、C語(yǔ)言中void*?可以與任意類(lèi)型指針?自動(dòng)轉(zhuǎn)換。
????2、C++中void*不能給其他類(lèi)型的指針直接賦值,必須強(qiáng)制類(lèi)型轉(zhuǎn)換,但其他類(lèi)型的指針可以自動(dòng)給void*賦值。
????3、C++為什么這樣修改void*?
????????為了更安全,所以C++類(lèi)型檢查更嚴(yán)格。
????????C++可以自動(dòng)識(shí)別類(lèi)型,對(duì)萬(wàn)能指針的需求不再那么強(qiáng)烈。
九、操作符別名
????某些特殊語(yǔ)言的鍵沒(méi)有~,&符合,所以C++標(biāo)準(zhǔn)委員會(huì)為了讓C++更有競(jìng)爭(zhēng)力,為符號(hào)定義了一些別名,讓這些小語(yǔ)種也可以愉快編寫(xiě)C++代碼
????and?????&&
????or??????||
????not?????!
????{???????<%????
????}???????%>
????#???????:%
十、函數(shù)重載(重載、隱藏、重寫(xiě))
????1、函數(shù)重載
????????在同一作用域下,函數(shù)名相同,參數(shù)列表不同的函數(shù),構(gòu)成重載關(guān)系。
????2、重載實(shí)現(xiàn)的機(jī)制
????????C++代碼在編譯時(shí)會(huì)把函數(shù)的參數(shù)類(lèi)型添加到參數(shù)名中,借助這個(gè)方式來(lái)實(shí)現(xiàn)函數(shù)重載,也就是C++的函數(shù)在編譯期間經(jīng)歷換名的過(guò)程。
????因此,C++代碼不能調(diào)用C函數(shù)(C語(yǔ)言編譯器編譯出的函數(shù))
????3、extern?"C"?{}
????????告訴C++編譯器按照C語(yǔ)言的方式聲明函數(shù),這樣C++就可以調(diào)用C編譯器編譯出的函數(shù)了(C++目標(biāo)文件可以與C目標(biāo)文件合并生成可執(zhí)行程序)。
????????如果C想調(diào)用C++編譯出的函數(shù),需要將C++函數(shù)的定義用extern?"C"包括一下。
????????注意:如果兩個(gè)函數(shù)名一樣,一定會(huì)沖突。
????4、重載和作用域
????????函數(shù)的重載關(guān)系發(fā)生在同一作用域下,不同作用域下的同名函數(shù),構(gòu)成隱藏關(guān)系。
????5、重載解析
????????當(dāng)調(diào)用函數(shù)時(shí),編譯器根據(jù)實(shí)參的類(lèi)型和形參的匹配情況,選擇一個(gè)確定的重載版本,這個(gè)過(guò)程叫重載解析。
????????實(shí)參的類(lèi)型和形參的匹配情況有三種:
????????????1、編譯器找到與實(shí)參最佳的匹配函數(shù),編譯器將生成調(diào)用代碼。
????????????2、編譯找不到匹配函數(shù),編譯器將給出錯(cuò)誤信息。
????????????3、編譯器找到多個(gè)匹配函數(shù),但沒(méi)有一個(gè)最佳的,這種錯(cuò)誤叫二義性。
????????在大多數(shù)情況下編譯器都能立即找到一個(gè)最佳的調(diào)用版本,但如果沒(méi)有,編譯就會(huì)進(jìn)行類(lèi)型提升,這樣備選函數(shù)中就可能具有多個(gè)可調(diào)用
的版本,這樣就可能產(chǎn)生二義性錯(cuò)誤。
????6、確定存在函數(shù)的三個(gè)步驟
????????1)候選函數(shù)
????????函數(shù)調(diào)用的第一步就是確定所有可調(diào)用的函數(shù)的集合(函數(shù)名、作用域),該集合中的函數(shù)就是候選函數(shù)。
????????2)選擇可行函數(shù)
????????從候選函數(shù)中選擇一個(gè)或多個(gè)函數(shù),選擇的標(biāo)準(zhǔn)是參數(shù)個(gè)數(shù)相同,而且通過(guò)類(lèi)型提升實(shí)參可被隱式轉(zhuǎn)換為形參。
????????3)尋找最佳匹配
????????優(yōu)先每個(gè)參數(shù)都完全匹配的方案,其次參數(shù)完全匹配的個(gè)數(shù),再其次是浪費(fèi)內(nèi)存的字節(jié)數(shù)。
????7、指針類(lèi)型會(huì)對(duì)函數(shù)重載造成影響
????????C++函數(shù)的形參如果是指針類(lèi)型,編譯時(shí)函數(shù)名中會(huì)追加Px。
十一、默認(rèn)形參
????1、在C++中函數(shù)的形參可以設(shè)置默認(rèn)值,調(diào)用函數(shù),如果沒(méi)有提供實(shí)參數(shù),則使用默認(rèn)形參。
????2、如果形參只有一部分設(shè)置了默認(rèn)形參,則必須靠右排列。
????3、函數(shù)的默認(rèn)形參是在編譯階段確定的,因此只能使用常量、常量表達(dá)式、全局變量數(shù)據(jù)作為默認(rèn)值。
????4、如果函數(shù)的聲明和定義需要分開(kāi),那么默認(rèn)形參設(shè)置在聲明、定義,還是聲明定義都需要設(shè)置。
????5、默認(rèn)形參會(huì)對(duì)函數(shù)重載造成影響,設(shè)置默認(rèn)形參時(shí)一定要慎重。
十二、內(nèi)聯(lián)函數(shù)
????1、普通函數(shù)調(diào)用時(shí)是生成調(diào)用指令(跳轉(zhuǎn)),然后當(dāng)代碼執(zhí)行到調(diào)用位置時(shí)跳轉(zhuǎn)到函數(shù)所在的代碼段執(zhí)行。
????2、內(nèi)聯(lián)函數(shù)就把函數(shù)編譯好的二進(jìn)制指令直接復(fù)制到函數(shù)的調(diào)用位置。
????3、內(nèi)聯(lián)函數(shù)的優(yōu)點(diǎn)就是提高程序的運(yùn)行速度(因?yàn)闆](méi)有跳轉(zhuǎn),也不需要返回),但這樣會(huì)導(dǎo)致可執(zhí)行文件增大(冗余),也就是犧牲空間來(lái)?yè)Q取時(shí)間。
????4、內(nèi)聯(lián)分為顯示內(nèi)聯(lián)和隱式內(nèi)聯(lián)
????????顯示內(nèi)聯(lián):在函數(shù)前?inline(C語(yǔ)言C99標(biāo)準(zhǔn)也支持)
????????隱式內(nèi)聯(lián):結(jié)構(gòu)、類(lèi)中內(nèi)部直接定義的成員函數(shù),則該類(lèi)型函數(shù)會(huì)被優(yōu)化成內(nèi)聯(lián)函數(shù)。
????5、宏函數(shù)在調(diào)用時(shí)會(huì)把函數(shù)體直接替換到調(diào)用位置,與內(nèi)聯(lián)函數(shù)一樣也是使用空間來(lái)?yè)Q取時(shí)間,所以宏函數(shù)與內(nèi)聯(lián)函數(shù)的區(qū)別(優(yōu)缺點(diǎn))?
????????1、宏函數(shù)不是真正的函數(shù),只是代碼替換,不會(huì)有參數(shù)壓棧、出棧以及返回值,也不會(huì)檢查參數(shù)類(lèi)型,因此所有類(lèi)型都能使用,但這樣會(huì)
有安全隱患。
????????2、內(nèi)聯(lián)函數(shù)是真正的函數(shù),被調(diào)用時(shí)會(huì)進(jìn)行傳參,會(huì)進(jìn)行壓棧、出棧,可以有返回值,并會(huì)嚴(yán)格檢查參數(shù)類(lèi)型,這樣就不能通用,如果想被
多種類(lèi)型調(diào)用需要重載。
????6、內(nèi)聯(lián)適用的條件
????????由于內(nèi)聯(lián)會(huì)造成可執(zhí)行文件變大,并增加內(nèi)存開(kāi)銷(xiāo),因此只有頻繁調(diào)用的簡(jiǎn)單函數(shù)適合作為內(nèi)聯(lián)。
????????調(diào)用比較少的復(fù)雜函數(shù),內(nèi)聯(lián)后并不顯著提高性能,不足以抵消犧牲空間帶來(lái)的損失,所以不適合內(nèi)聯(lián)。
????????帶有遞歸特性和動(dòng)態(tài)綁定特性的函數(shù),無(wú)法實(shí)施內(nèi)聯(lián),因此編譯器會(huì)忽略聲明部分的inline關(guān)鍵字。
十三、引用
????引用就是取藝名(別名)。
????1、引用的基本特性
????????引用就是取別名,聲明一個(gè)標(biāo)識(shí)符為引用,就表示該標(biāo)識(shí)符是另一個(gè)對(duì)象的外號(hào)。
????????1、引用必須初始化,不存在空引用,但有懸空引用(變量死了,名還留著)。
????????2、可以引用無(wú)名對(duì)象和臨時(shí)對(duì)象,但必須使用常引用。
????????3、引用不能更換目標(biāo)
????????4、引用目標(biāo)如果具有const屬性,引用也需要具有const屬性。
????????引用一旦完成了定義和初始化就和普通變量名一樣,它就代表了目標(biāo),一經(jīng)引用終身不能再引用其他目標(biāo)。
????2、引用型參數(shù)
????????引用當(dāng)作函數(shù)的參數(shù)能達(dá)到指針同樣的效果,但不具備指針的危險(xiǎn),還比指針?lè)奖恪?/p>
????????引用可以非常簡(jiǎn)單的實(shí)現(xiàn)函數(shù)間共享變量的目的,而且是否使用引用由被調(diào)函數(shù)說(shuō)了算。
????????引用當(dāng)作函數(shù)的參數(shù)還能提高傳遞參數(shù)效率,指針至少還需要4字節(jié)內(nèi)存,而引用只需要增加一條標(biāo)識(shí)符與內(nèi)存之間的綁定(映射)。
????3、引用型返回值
????????不要返回局部變量的引用,會(huì)造成懸空引用。
????????如果返回值是一個(gè)臨時(shí)值(右值),如果非要使用引用接收的話(huà),必須使用常引用。
????注意:C++中的引用時(shí)一種取別名的機(jī)制,而C語(yǔ)言中的指針是一種數(shù)據(jù)類(lèi)型(代表內(nèi)存編號(hào)的無(wú)符號(hào)整數(shù))。
????練習(xí)1:實(shí)現(xiàn)一個(gè)C++版本的swap函數(shù)。
????指針和引用的相同點(diǎn)和不同點(diǎn):
????????相同點(diǎn):跨函數(shù)共享變量,優(yōu)化傳參效率,避免傳參的時(shí)候調(diào)用拷貝構(gòu)造
????????不同點(diǎn):指針有自己的存儲(chǔ)空間,借助指針可以使用堆內(nèi)存,引用不行。引用取別名,指針是數(shù)據(jù)類(lèi)型。指針可以為空,引用不可以為空。
指針可以不初始化,引用必須初始化。指針可以改變指向,引用不能引用其他對(duì)象(可以定義指針的指針,不能定義引用的引用??梢远x指針的
引用,不能定義引用的指針??梢远x指針的數(shù)組,但不能定義引用的數(shù)組??梢远x數(shù)組的引用)。
void?swap(int&?a,int&?b)?//引用
{
????int?temp?=?a;
????a?=?b;
????b?=?temp;
}
int?main()
{
????int?a=3,b=4;
????swap(a,b);
????cout<<a<<"?"<<b<<endl;
}
????
十四、C++的內(nèi)存管理
????1、new/delete?C++具備申請(qǐng)/釋放堆內(nèi)存功能的運(yùn)算符
????????相當(dāng)于C語(yǔ)言中的malloc和free。
????????new?類(lèi)型:會(huì)自動(dòng)計(jì)算類(lèi)型所需要字節(jié)數(shù),然后從堆中分配對(duì)應(yīng)字節(jié)數(shù)的內(nèi)存,并返回內(nèi)存的首地址(具備類(lèi)型)。
????????
????????delete?指針:會(huì)自動(dòng)釋放堆內(nèi)存。
????????注意:new/delete與malloc/free不能混用,因?yàn)閚ew和delete會(huì)自動(dòng)調(diào)用類(lèi)、結(jié)構(gòu)的構(gòu)造函數(shù)、析構(gòu)函數(shù)。
????2、數(shù)組的分配與釋放
????????new?類(lèi)型[n];?n表示數(shù)組長(zhǎng)度,如果類(lèi)、結(jié)構(gòu)會(huì)自動(dòng)調(diào)用n次構(gòu)造函數(shù)。
????????delete[]?指針;通過(guò)new[]?分配的內(nèi)存,必須通過(guò)delete[]釋放。
????????new[]?返回值前4個(gè)字節(jié)中存放著數(shù)組的長(zhǎng)度。
????3、重復(fù)釋放
????????delete/delete[]不能重復(fù)釋放同一塊內(nèi)存。
????????delete/delete[]釋放野指針的后果不確定,但釋放空指針是安全的。
struct?Student
{
????Student(void)
????{
????????cout<<"我是構(gòu)造函數(shù),創(chuàng)建對(duì)象時(shí),我就會(huì)執(zhí)行"?<<?endl;
????}
????~Student(void)
????{
????????cout<<?"我是析構(gòu)函數(shù),釋放對(duì)象時(shí),我就會(huì)執(zhí)行"?<<?endl;
????}
};
int?main()
{
????int*?p?=?new?int;
????*p?=?10;
????cout<<?*p?<<endl;
????Student?stu;
????
????Student*?s?=?new?Student;
????delete(s);
????cout?<<?endl;
????Student*?a?=?new?Student[3];
????p?=?(int*)a;
????cout?<<?*(p-1)?<<?endl;
????delete[]?a;?
}
????4、內(nèi)存分配失敗
????????當(dāng)分配的內(nèi)存過(guò)大,沒(méi)有能滿(mǎn)足需求的整塊內(nèi)存就會(huì)拋出異常,std::bad_alloc。
????????new/delete和C語(yǔ)言的malloc/free的相同點(diǎn)和不同點(diǎn)(區(qū)別)?
????????不同點(diǎn):
????????????身份?????運(yùn)算符???????????????標(biāo)準(zhǔn)庫(kù)函數(shù)
????????????參數(shù)?????類(lèi)型(自動(dòng)計(jì)算)??????字節(jié)數(shù)(手動(dòng)計(jì)算)
????????????返回值???帶類(lèi)型的地址??????????void*地址
????????????調(diào)用構(gòu)造?自動(dòng)調(diào)用??????????????不能調(diào)用構(gòu)造/析構(gòu)函數(shù)
????????????出錯(cuò)?????拋出異常??????????????返回NULL
????????相同點(diǎn):
????????????1、都能管理堆內(nèi)存
????????????2、不能重復(fù)釋放
????????????3、可以釋放NULL
????
????注意:在C++中盡量使用引用、new/delete
int?main()
{
????int?*p?=?NULL;
????try{
????????p?=?new?int[~0];
????}
????catch(std::bad_alloc&?ex)
????{
????????cout?<<?"error"?<<?endl;
????}
}
十五、強(qiáng)制類(lèi)型轉(zhuǎn)換
略

面向過(guò)程編程:
????關(guān)注是問(wèn)題解決的過(guò)程步驟,算法
面向?qū)ο缶幊蹋?/h1>
????關(guān)注的是誰(shuí)能解決問(wèn)題(類(lèi)),需要什么樣的數(shù)據(jù)(成員變量),具備什么樣的技能(成員函數(shù))才能解決問(wèn)題。
????抽象:找出一個(gè)能夠解決問(wèn)題的“對(duì)象”(觀察研究對(duì)象),找出解決所必須的數(shù)據(jù)(屬性)、功能(成員函數(shù))。
????封裝:把抽象的結(jié)果,歸結(jié)為一個(gè)類(lèi)(數(shù)據(jù)類(lèi)型),然后實(shí)例化出類(lèi)對(duì)象,設(shè)置對(duì)象的屬性,調(diào)用對(duì)象的功能達(dá)到解決問(wèn)題的目的。
????繼承:在解決問(wèn)題前,先尋找之前的類(lèi)能不能解決問(wèn)題,或解決部分問(wèn)題,如果可以則把舊的類(lèi)繼承后再次拓展,來(lái)縮短解決問(wèn)題的時(shí)間,降低
解決問(wèn)題的難度。
????多態(tài):對(duì)象的多種形態(tài),外部看到一個(gè)對(duì)象發(fā)出指令,對(duì)象會(huì)根據(jù)自身情況做出獨(dú)特的反應(yīng)。
一、類(lèi)和對(duì)象
????1、通過(guò)分析對(duì)象的屬性和行為設(shè)計(jì)出一個(gè)類(lèi)。
????2、類(lèi)就是數(shù)據(jù)類(lèi)型
????????簡(jiǎn)單類(lèi)型:只能表示一個(gè)屬性(變量),C/C++內(nèi)建數(shù)據(jù)類(lèi)型
????????數(shù)組類(lèi)型:可以表示多個(gè)屬性(變量),類(lèi)型必須相同。
????????結(jié)構(gòu)類(lèi)型:可以表示多個(gè)屬性(變量),但缺少行為(函數(shù))。
????????類(lèi)類(lèi)型:即能表示屬性,也能表示行為,一直復(fù)合數(shù)據(jù)類(lèi)型。
????3、對(duì)象就是類(lèi)這種數(shù)據(jù)類(lèi)型創(chuàng)建出的實(shí)例,相當(dāng)于結(jié)構(gòu)變量。
????class?Student
????{
????????屬性(成員變量);
????????行為(成員函數(shù));
????};
????Student?stu;
二、類(lèi)的定義與實(shí)例化
????1、類(lèi)的一般形式
????class?類(lèi)名?:?繼承方式?父類(lèi)
????{
????public/private/protected:?//?訪(fǎng)問(wèn)控制限制符
????????成員變量;
????????//?構(gòu)造函數(shù)
????????類(lèi)名(形參表)
????????{
????????????
????????}
????????//?析構(gòu)函數(shù)
????????~類(lèi)名(void)
????????{
????????}
????};
????2、類(lèi)的訪(fǎng)問(wèn)控制限定符
????public:公有成員,在任何位置都可以訪(fǎng)問(wèn)
????private:私有成員,只能在類(lèi)(自己)的成員函數(shù)中訪(fǎng)問(wèn)
????protected:受保護(hù)成員,只能在類(lèi)(自己)和子類(lèi)中訪(fǎng)問(wèn)
????注意:類(lèi)中的成員變量、成員函數(shù)默認(rèn)是?private,結(jié)構(gòu)中的成員和成員函數(shù)默認(rèn)是?public。
????注意:C++中類(lèi)和結(jié)構(gòu)的區(qū)別只有成員函數(shù)和成員變量的默認(rèn)訪(fǎng)問(wèn)權(quán)限不同。
????3、構(gòu)造函數(shù)
????????1)什么是構(gòu)造函數(shù):類(lèi)的同名函數(shù)就是構(gòu)造函數(shù),沒(méi)有返回值。
????????2)什么時(shí)候調(diào)用,誰(shuí)調(diào)用,調(diào)用幾次?
????????????創(chuàng)建類(lèi)對(duì)象時(shí)會(huì)被自動(dòng)調(diào)用(每創(chuàng)建一個(gè)類(lèi)對(duì)象,就會(huì)調(diào)用一次),對(duì)象整個(gè)生命周期中一定會(huì)被
調(diào)用一次,只能被調(diào)用一次。
????????3)負(fù)責(zé)干什么
????????????成員變量的初始化,分配相關(guān)資源,設(shè)置對(duì)象的初始狀態(tài)。
????????class?類(lèi)名?:?繼承方式?父類(lèi)
????????{
????????????//?構(gòu)造函數(shù)
?????????類(lèi)名(形參表)
????????????{
????????????
????????????}
????????};
????4、類(lèi)型的創(chuàng)建過(guò)程
????????1.分配類(lèi)型所需要空間,無(wú)論棧還是堆。
????????2.傳遞實(shí)參調(diào)用構(gòu)造函數(shù),完成如下任務(wù):
????????????1)根據(jù)繼承表依次調(diào)用父類(lèi)的構(gòu)造函數(shù)
????????????2)根據(jù)成員變量的順序依次調(diào)用成員變量的構(gòu)造函數(shù)。
????????????3)執(zhí)行構(gòu)造函數(shù)體中的代碼。
????注意:執(zhí)行構(gòu)造函數(shù)的代碼是整個(gè)構(gòu)造函數(shù)的最后一步。
????要保證構(gòu)造函數(shù)代碼所需要的一切資源和先決條件在該代碼執(zhí)行前已經(jīng)準(zhǔn)備充分,并得到正確的初始化。
????5、對(duì)象的創(chuàng)建方法
????????1.在棧上創(chuàng)建:類(lèi)名?對(duì)象;//?不需要括號(hào)
????????????類(lèi)名?對(duì)象(實(shí)參);
????????2.在堆上創(chuàng)建:類(lèi)名*?對(duì)象指針?=?new?類(lèi)名;
????????????類(lèi)名*?對(duì)象指針?=?new?類(lèi)名(實(shí)參);
????????3.創(chuàng)建多個(gè)對(duì)象:
????????????類(lèi)名?對(duì)象?=?{類(lèi)名(實(shí)參),類(lèi)名(實(shí)參),類(lèi)名(實(shí)參)};
????????????類(lèi)名*?對(duì)象指針?=?new?類(lèi)名[n]{類(lèi)名(實(shí)參),類(lèi)名(實(shí)參)};
????????注意:通過(guò)malloc創(chuàng)建的類(lèi)對(duì)象不能調(diào)用構(gòu)造函數(shù)。
????????注意:通過(guò)new[]創(chuàng)建的對(duì)象,一定要通過(guò)delete[]釋放。
????6、類(lèi)的聲明、實(shí)現(xiàn)、調(diào)用
????????1.在頭文件中聲明
????????class?類(lèi)名?:?繼承方式?父類(lèi)
????????{
????????????成員變量;
????????public:?//?訪(fǎng)問(wèn)控制限制符
????????????//?構(gòu)造函數(shù)
????????????類(lèi)名(形參表);
????????????//?析構(gòu)函數(shù)
????????????~類(lèi)名(void);
????????????//?其他成員函數(shù)
????????????返回值?函數(shù)名(參數(shù)列表);
????????};
????????2.源文件實(shí)現(xiàn)類(lèi)的相關(guān)函數(shù)
????????返回值?類(lèi)名::函數(shù)名(參數(shù)列表)
????????{
????????}
????????3.調(diào)用時(shí)只需要導(dǎo)入頭文件,然后與類(lèi)函數(shù)所在的源文件一起編譯即可。
????????注意:如果一個(gè)類(lèi)內(nèi)容不多,可以考慮在頭文件中完全實(shí)現(xiàn)。
????????????也可以只在頭文件中實(shí)現(xiàn)一些簡(jiǎn)單的成員函數(shù)。
????????注意:類(lèi)中自動(dòng)生成的函數(shù),在源文件中實(shí)現(xiàn)時(shí),也需要在頭文件中聲明。
????class和struct的區(qū)別?
????????class的默認(rèn)繼承和訪(fǎng)問(wèn)權(quán)限是private,struct的默認(rèn)繼承和訪(fǎng)問(wèn)權(quán)限是public。class能做模板的參數(shù),struct不行。
三、構(gòu)造函數(shù)與初始化列表
????1、構(gòu)造函數(shù)可以被重載(同一個(gè)名字的函數(shù)有多個(gè)不同版本)
????2、缺省構(gòu)造是編譯器自動(dòng)生成的一個(gè)什么都不做的構(gòu)造函數(shù)(唯一的作用就是避免編譯錯(cuò)誤)。
????注意:當(dāng)類(lèi)實(shí)現(xiàn)一個(gè)有參構(gòu)造時(shí),缺省構(gòu)造就不會(huì)再自動(dòng)生成,如果有需要必須顯示地寫(xiě)出來(lái)。
????3、無(wú)參構(gòu)造未必?zé)o參,當(dāng)給有參構(gòu)造的所有參數(shù)設(shè)置默認(rèn)形參,調(diào)用這種構(gòu)造函數(shù)就不需要傳參。
????注意:所謂的“編譯器生成的某某函數(shù)”其實(shí)不是真正語(yǔ)法意義上的函數(shù),而是功能意義上的函數(shù),編譯器作為可執(zhí)行指令的生成者,它會(huì)直接生成
具有某項(xiàng)功能的二進(jìn)制指令,不需要借助高級(jí)語(yǔ)言語(yǔ)義上的函數(shù)完成此任務(wù)。
????注意:如果一個(gè)類(lèi)是其他類(lèi)的成員變量,那么一定要保證它有一個(gè)無(wú)參構(gòu)造,當(dāng)B的構(gòu)造函數(shù)執(zhí)行時(shí)會(huì)執(zhí)行成員變量的無(wú)參構(gòu)造,而此時(shí)類(lèi)B是無(wú)
法給類(lèi)A成員變量提供參數(shù)的。
????4、單參構(gòu)造與類(lèi)型轉(zhuǎn)換
????????如果構(gòu)造函數(shù)的參數(shù)只有一個(gè),那么Test?t?=?n語(yǔ)句就不會(huì)出錯(cuò),它會(huì)自動(dòng)調(diào)用單參構(gòu)造來(lái)達(dá)到類(lèi)型轉(zhuǎn)換的效果。
????????如果想禁止這種類(lèi)型轉(zhuǎn)換需要在單參構(gòu)造前加?explicit
????5、初始化列表
????????為類(lèi)成員進(jìn)行初始化用的。
????????構(gòu)造函數(shù)(參數(shù)):成員1(參數(shù)1),成員2(參數(shù)2)...
????????const?int?num;
????????Test(int?n):num(n)
????????{
????????}
????????通過(guò)初始化列表可以給類(lèi)成員變量傳遞參數(shù),以此調(diào)用類(lèi)成員的有參構(gòu)造。
????????初始化列表也可以給?const?成員、引用成員進(jìn)行初始化。
class?A
{
public:
????int?num;
????A(int?_num)
????{
????????num?=?_num;
????????cout<<"我A的有參構(gòu)造"<<endl;
????}
};
class?Test
{
public:
????string?str;
????const?int?num;
????int&?xiu;
????A?a;
????Test(int?num,const?char*?str,int&?x):num(num),str(str),a(num),xiu(x)
????{
????????cout<<"---"<<endl;
????}
};
int?main()
{
????int?x?=?100;
????Test?t(10,"aa",x);
????//Test?t?=?{10};
????//t.num?=?100;
????cout<<?t.num?<<?endl;
????cout<<t.xiu<<endl;
????x?=?1000;
????cout<<t.xiu<<endl;
}
????????成員的初始化順序與初始化列表沒(méi)有關(guān)系,而是在類(lèi)中的定義順序有關(guān)。
class?A
{
public:
????A(int?n)
????{
????????cout<<"A"<<endl;
????}
};
class?B
{
public:
????B(int?n)
????{
????????cout<<"B"<<endl;
????}
};
class?C
{
public:
????C(int?n)
????{
????????cout<<"C"<<endl;
????}
};
class?Test
{
public:
????A?a;
????C?c;
????B?b;
????//A?a;
????Test(int?c1,int?a1,int?b1):c(c1),a(a1),b(b1)
????{
????}
};
int?main()
{
//??A?a1;
//??B?b1;
//??C?c1;
????Test?t(1,2,3);
}
????????注意:初始化列表運(yùn)行類(lèi)成員變量還沒(méi)有定義成功。
作業(yè)1:封裝一個(gè)List類(lèi)
附加題:以C++編程方式實(shí)現(xiàn)2048游戲。
一、this指針
????類(lèi)的成員變量單獨(dú)存儲(chǔ)在每個(gè)類(lèi)對(duì)象中,成員函數(shù)存儲(chǔ)在代碼段中,所有的類(lèi)對(duì)象共享一份成員函數(shù)。
????
????成員函數(shù)是如何區(qū)別調(diào)用它的是哪個(gè)類(lèi)對(duì)象的?
????答:借助了this指針,類(lèi)的每個(gè)成員函數(shù)都有一個(gè)隱藏的參數(shù)this指針,它指向類(lèi)對(duì)象。
????類(lèi)的構(gòu)造函數(shù)中也同樣有this指針,指向的就是正在構(gòu)造的這個(gè)對(duì)象。
????在類(lèi)中(成員、構(gòu)造、析構(gòu)函數(shù))對(duì)成員變量、成員函數(shù)的訪(fǎng)問(wèn)都是借助了this指針。
????this指針是隱藏的,但也可以顯示使用:
????????1、參數(shù)與成員一樣時(shí),使用this可以區(qū)別出成員與參數(shù)名。
????????2、在成員函數(shù)中如果想返回當(dāng)前對(duì)象的指針、引用等,可以使用this指針實(shí)現(xiàn)。
????????3、將this指針作為函數(shù)的參數(shù),從一個(gè)對(duì)象傳遞給另一個(gè)其它類(lèi)對(duì)象,可以實(shí)現(xiàn)對(duì)象間的交互。
#include?<cstring>
class?User
{
????char?name[20];
????char?pass[7];
public:
????User(const?char*?name,const?char*?pass)
????{
????????strcpy(this->name,name);
????????strcpy(this->pass,pass);
????????//show();
????}
????User&?func(void)
????{
????????return?*this;
????}
????void?show(void)//隱藏this指針
????{
????????cout<<?name?<<?"?"<<?pass?<<endl;
????}
????User*?this?=?const?this;
};
int?main()
{
????User?u1("aaa","123");
????User?u2("bbb","321");
????User&?u3?=?u1.func();
????u1.show();
????u2.show();
????u3.show();
}
二、常函數(shù)
????在函數(shù)的參數(shù)列表與函數(shù)體之間有const修飾的函數(shù),這個(gè)const其實(shí)就是在修飾this指針。
????不能在常函數(shù)內(nèi)修改成員變量的值,普通成員函數(shù)可以調(diào)用常函數(shù),而常函數(shù)只能調(diào)用常函數(shù)。
????如果在常函數(shù)中真的需要修改某個(gè)成員變量的數(shù)據(jù),那么需要這個(gè)成員被?mutable修飾。
mutable?char?name[20];
void?show(void)?const//隱藏this指針
{
????strcpy(name,"------");
????cout<<?name?<<?"?"<<?pass?<<endl;
}
????
????普通函數(shù)不能聲明為常函數(shù)(因?yàn)闆](méi)有this指針)。
三、析構(gòu)函數(shù)
????1、特殊的成員函數(shù)?
????????~類(lèi)名(void)
????????{
????????}
????????沒(méi)有參數(shù)、沒(méi)有返回值、不能重載
????2、誰(shuí)來(lái)調(diào)用
????????析構(gòu)函數(shù)會(huì)在銷(xiāo)毀對(duì)象時(shí)自動(dòng)調(diào)用,在對(duì)象的整個(gè)生命周期內(nèi)最多被調(diào)用一次。
????3、析構(gòu)函數(shù)負(fù)責(zé)什么
????????負(fù)責(zé)釋放在構(gòu)造函數(shù)期間獲取的所有資源,它的執(zhí)行過(guò)程:
????????1.先執(zhí)行析構(gòu)函數(shù)本身代碼
????????2.調(diào)用成員類(lèi)的析構(gòu)函數(shù)
????????3.調(diào)用父類(lèi)的析構(gòu)函數(shù)
#include?<cstring>
#include?<cstdlib>
class?A
{
public:
????A(void)
????{
????????cout?<<?"A?'s?構(gòu)造"?<<endl;
????}
????~A(void)
????{
????????cout<<"A?'s?析構(gòu)"<<endl;
????}
};
class?B
{
public:
????B(void)
????{
????????cout?<<?"B?'s?構(gòu)造"?<<endl;
????}
????~B(void)
????{
????????cout<<"B?'s?析構(gòu)"<<endl;
????}
};
class?User?:?public?A
{
????char*?name;
????char*?pass;
????B?b;
????//char?name[20];
????//char?pass[10];
public:
????User(const?char*?name,const?char*?pass)
????{
????????this->name?=?new?char[strlen(name)+1];
????????strcpy(this->name,name);
????????this->pass?=?new?char[strlen(pass)+1];
????????strcpy(this->pass,pass);
????????cout<<?"構(gòu)造"<<endl;
????????cout<<"-----"<<endl;
????}
/*??User(void)
????{
????????cout<<"構(gòu)造"<<endl;
????}
*/
????~User(void)
????{
????????delete?name;
????????delete?pass;
????????cout<<"析構(gòu)"<<endl;
????}
};
int?main()
{
????User*?u1?=?new?User("asd","ads");
????//exit(0);
????delete?u1;
????//User?u2;
}
????4.缺省的析構(gòu)函數(shù)
????????如果一個(gè)類(lèi)沒(méi)有實(shí)現(xiàn)析構(gòu)函數(shù),編譯器會(huì)自動(dòng)生成一個(gè)具有析構(gòu)函數(shù)功能的二進(jìn)制指令,它負(fù)責(zé)釋放編譯器能夠看得到的資源(成員變量、
類(lèi)成員、弗雷成員),這就是缺省析構(gòu)。
????????如果類(lèi)中沒(méi)有動(dòng)態(tài)資源,也不需要做善后工作,缺省析構(gòu)就完全共用了,不需要再實(shí)現(xiàn)新析構(gòu)函數(shù)。
????????注意:缺省析構(gòu)無(wú)法釋放動(dòng)態(tài)資源(堆內(nèi)存)【堆內(nèi)存是動(dòng)態(tài)資源,動(dòng)態(tài)資源不一定是堆內(nèi)存】
????作業(yè):類(lèi)對(duì)象的創(chuàng)建過(guò)程與釋放過(guò)程。
????????創(chuàng)建:分配內(nèi)存(對(duì)象)->?父類(lèi)構(gòu)造->?成員構(gòu)造->?自己構(gòu)造
????????????父類(lèi)構(gòu)造:按照繼承表從左到右依次構(gòu)造。
????????????成員構(gòu)造:按照聲明順序從上至下依次構(gòu)造。
????????釋放:自己析構(gòu)->?成員析構(gòu)->?父類(lèi)析構(gòu)->?釋放內(nèi)存(對(duì)象)
????????????成員析構(gòu):按照聲明順序從下到上依次構(gòu)造。
????????????父類(lèi)析構(gòu):按照繼承表從右到左依次構(gòu)造。
四、拷貝構(gòu)造
????拷貝構(gòu)造又稱(chēng)為復(fù)制構(gòu)造,是一種特殊的構(gòu)造函數(shù),它是使用一個(gè)現(xiàn)有的舊對(duì)象構(gòu)造一個(gè)新的對(duì)象時(shí)調(diào)用的函數(shù),只有一個(gè)引用型的參數(shù)(對(duì)象本身)。
????類(lèi)名(類(lèi)&?)
????{
????}
????拷貝構(gòu)造的參數(shù)應(yīng)該加?const?保護(hù),但編譯器并沒(méi)有強(qiáng)行限制。
????編譯器會(huì)自己生成一個(gè)拷貝構(gòu)造函數(shù),它負(fù)責(zé)把舊對(duì)象中的所有數(shù)據(jù)拷貝給新創(chuàng)建的對(duì)象。
????深拷貝與淺拷貝的區(qū)別:
????????如果類(lèi)成員有指針,淺拷貝只拷貝指針變量的值,而深拷貝指針變量所指向的目標(biāo)。
????什么情況下需要實(shí)現(xiàn)拷貝構(gòu)造:
????????當(dāng)類(lèi)成員中沒(méi)有指針成員,此時(shí)默認(rèn)的拷貝構(gòu)造(淺拷貝)就無(wú)法完成任務(wù),需要自己動(dòng)手實(shí)現(xiàn)拷貝構(gòu)造(深拷貝)。
????什么情況下會(huì)調(diào)用拷貝構(gòu)造:
????????1、使用舊對(duì)象給新對(duì)象賦值時(shí)
????????User?user1?=?user;
????????2、使用對(duì)象當(dāng)作函數(shù)的參數(shù),當(dāng)調(diào)用函數(shù)時(shí),就會(huì)一起調(diào)用拷貝構(gòu)造。
#include?<cstring>
class?User
{
????char*?name;
????char?pass[7];
????int?id;
public:
????User(const?char*?name,const?char*?pass)
????{
????????this->name?=?new?char[strlen(name)+1];
????????strcpy(this->name,name);
????????strcpy(this->pass,pass);
????}
????void?show(void)
????{
????????cout<<name?<<"?"?<<pass?<<endl;
????}
????~User(void)
????{
????????cout<<"析構(gòu)"<<&name?<<endl;
????????delete[]?name;
????}
????User(User&?that)
????{
????????name?=?new?char[strlen(that.name)];
????????strcpy(name,that.name);
????????strcpy(pass,that.pass);
????????cout?<<?"我是拷貝構(gòu)造"?<<?endl;
????}
};
void?func(User&?user)
{
????user.show();
}
int?main()
{
????User?u1("a","aa");
????u1.show();
????//?調(diào)用拷貝構(gòu)造
????User?u2?=?u1;
????u2.show();
????func(u1);
}
五、賦值構(gòu)造(賦值運(yùn)算符)
????當(dāng)一類(lèi)對(duì)象給另一個(gè)類(lèi)對(duì)象賦值時(shí),就會(huì)調(diào)用賦值構(gòu)造
????void?opeator?=?(類(lèi)&)
????{
????}
????什么時(shí)會(huì)調(diào)用:對(duì)象?=?對(duì)象;
????編譯器會(huì)生成一個(gè)缺省的賦值構(gòu)造,它負(fù)責(zé)把一個(gè)對(duì)象的內(nèi)存拷貝給另一個(gè)對(duì)象。
????什么情況需要實(shí)現(xiàn)賦值構(gòu)造:
????????當(dāng)需要深拷貝時(shí),需要自己動(dòng)手實(shí)現(xiàn)賦值構(gòu)造,也就是拷貝構(gòu)造與賦值構(gòu)造需要同時(shí)實(shí)現(xiàn)。
????編譯器會(huì)自動(dòng)生成四個(gè)成員函數(shù):構(gòu)造、析構(gòu)、賦值構(gòu)造、拷貝構(gòu)造。? ??
#include?<cstring>
class?User
{
????char*?name;
????char?pass[7];
public:
????User(const?char*?name,const?char*?pass)
????{
????????this->name?=?new?char[strlen(name)+1];
????????strcpy(this->name,name);
????????strcpy(this->pass,pass);
????}
????void?show(void)
????{
????????cout<<name?<<"?"?<<pass?<<endl;
????}
????~User(void)
????{
????????cout<<"析構(gòu)"<<&name?<<endl;
????????delete[]?name;
????}
????User(User&?that)
????{
????????name?=?new?char[strlen(that.name)];
????????strcpy(name,that.name);
????????strcpy(pass,that.pass);
????????cout?<<?"我是拷貝構(gòu)造"?<<?endl;
????}
????
????User&?operator?=?(const?User&?that)
????{
????????cout<<?this?<<"?"<<?&that?<<?endl;
????????if(this?!=?&that)
????????{
????????????cout<<"我是賦值構(gòu)造"<<endl;
????????????//?釋放舊空間
????????????delete[]?name;
????????
????????????//?申請(qǐng)新空間
????????????name?=?new?char[strlen(that.name)+1];
????????????//?拷貝內(nèi)容
????????????strcpy(name,that.name);
????????????strcpy(pass,that.pass);
????????????
????????????/*
????????????User?temp(that);
????????????swap(name,temp.name);
????????????*/
????????}
????????return?*this;
????}
};
int?main()
{
????User?u1("a","aa");
????User?u2("bbbb","bb");
????User?u3("ccc","cc");
????//賦值構(gòu)造
????u1?=?u1;
????//u2?=?u1?=?u3;
????u1.show();
????u2.show();
????u3.show();
????
}
六、關(guān)于拷貝構(gòu)造、賦值構(gòu)造的建議
????1、缺省的拷貝構(gòu)造、賦值構(gòu)造函數(shù)不光會(huì)拷貝本類(lèi)的數(shù)據(jù),也會(huì)調(diào)用成員類(lèi)對(duì)象和父類(lèi)的拷貝構(gòu)造和賦值構(gòu)造,而不是單純的按字節(jié)復(fù)制,
因此盡量少用指針成員。
????2、在函數(shù)參數(shù)中,盡量使用類(lèi)指針或引用來(lái)當(dāng)參數(shù)(不要直接使用類(lèi)對(duì)象),減少調(diào)用拷貝構(gòu)造和賦值構(gòu)造的機(jī)會(huì),也可以降低數(shù)據(jù)傳遞的開(kāi)銷(xiāo)。
????3、如果由于特殊原因無(wú)法實(shí)現(xiàn)完整的拷貝構(gòu)造、賦值構(gòu)造,建議將它們私有化,防止誤用。
????4、一旦為一個(gè)類(lèi)實(shí)現(xiàn)了拷貝構(gòu)造,那么也一定要實(shí)現(xiàn)賦值構(gòu)造。(<=>)
七、靜態(tài)成員
????類(lèi)成員一旦被?static?修飾就會(huì)變成靜態(tài)成員,而是單獨(dú)一份存儲(chǔ)在bss或data內(nèi)存段中,所有的類(lèi)對(duì)象共享(靜態(tài)成員屬于類(lèi),而不屬于某個(gè)對(duì)象)。
????靜態(tài)成員在類(lèi)內(nèi)聲明,但必須在類(lèi)外定義、初始化。與成員函數(shù)一樣需要加“類(lèi)名::”限定符表示它屬于哪個(gè)類(lèi),但不需要再額外增加?static
????成員函數(shù)也可以被static修飾,這種函數(shù)叫靜態(tài)成員函數(shù),這種成員沒(méi)有this指針,因此在靜態(tài)函數(shù)中不能直接訪(fǎng)問(wèn)類(lèi)的成員,但可以直接訪(fǎng)問(wèn)
靜態(tài)成員,但可以直接訪(fǎng)問(wèn)靜態(tài)成員變量、靜態(tài)成員函數(shù)。
????靜態(tài)成員變量、函數(shù)依然受訪(fǎng)問(wèn)控制限定符的影響。
????因?yàn)樵诖a編譯完成后,靜態(tài)成員已經(jīng)定義完成(有了存儲(chǔ)空間),一次可以不用活類(lèi)對(duì)象而直接調(diào)用,類(lèi)名::靜態(tài)成員名
????靜態(tài)成員變量可以被當(dāng)做全局變量來(lái)使用(訪(fǎng)問(wèn)限定符必須是public),靜態(tài)成員函數(shù)可以當(dāng)作類(lèi)的接口,實(shí)現(xiàn)對(duì)類(lèi)的管理。
八、單例模式
????什么是單例模式,只能創(chuàng)建出一個(gè)類(lèi)對(duì)象(只有一實(shí)際的實(shí)例)的叫單例模式。
????單例模式的應(yīng)用場(chǎng)景:
????????Windows系統(tǒng)的任務(wù)管理器
????????Linux/Unix系統(tǒng)的日志系統(tǒng)
????????網(wǎng)站的訪(fǎng)問(wèn)計(jì)數(shù)器
????????服務(wù)端程序的連接池、線(xiàn)程池、數(shù)據(jù)池
????獲取單一對(duì)象的方法:
????????1、定義全局(C語(yǔ)言),但不受控制,防君子不能防小人。
????????2、專(zhuān)門(mén)寫(xiě)一個(gè)類(lèi),把類(lèi)的構(gòu)造函數(shù)設(shè)置私有,借助靜態(tài)成員函數(shù)提供一個(gè)接口,以此來(lái)獲取唯一的實(shí)例。
????C++如何實(shí)現(xiàn)單例:
????????1、禁止類(lèi)的外部創(chuàng)建類(lèi)對(duì)象:構(gòu)造函數(shù)設(shè)置私有
????????2、類(lèi)自己維護(hù)一個(gè)唯一的實(shí)例:使用靜態(tài)指針指向
????????3、提供一個(gè)獲取實(shí)例的方法:靜態(tài)成員函數(shù)獲取靜態(tài)指針
????餓漢模式:
????????將單例類(lèi)的唯一實(shí)例對(duì)象定義為成員變量,當(dāng)程序開(kāi)始運(yùn)行時(shí),實(shí)例對(duì)象就已經(jīng)創(chuàng)建完成
????????優(yōu)點(diǎn):加載進(jìn)程時(shí),靜態(tài)創(chuàng)建單例對(duì)象,線(xiàn)程安全。
????????缺點(diǎn):無(wú)論使用與否,總要?jiǎng)?chuàng)建,浪費(fèi)內(nèi)存。
????懶漢模式:
????????用靜態(tài)成員指針來(lái)指向單例類(lèi)的唯一實(shí)例對(duì)象,只有真正調(diào)用獲取實(shí)例的靜態(tài)接口時(shí),實(shí)例對(duì)象才被創(chuàng)建。
????????優(yōu)點(diǎn):什么時(shí)候用什么時(shí)候創(chuàng)建,節(jié)約內(nèi)存。
????????缺點(diǎn):在第一次調(diào)用獲取實(shí)例對(duì)象的靜態(tài)接口時(shí),才真正創(chuàng)建,如果在多線(xiàn)程操作情況下有可能被創(chuàng)建出多個(gè)實(shí)例對(duì)象(雖然可能性很低),
存在線(xiàn)程不安全問(wèn)題。
總結(jié):C語(yǔ)言與C++有哪些不同點(diǎn)
????內(nèi)存管理??malloc/free??new/delete
????static
????const
????void*
????字符串:string系列函數(shù)??string類(lèi)?
一、操作符函數(shù)重載
????什么是操作符函數(shù):在C++中針對(duì)類(lèi)類(lèi)型的對(duì)象的運(yùn)算符,由于它們肯定不支持真正的運(yùn)算操作,因此編譯器會(huì)將它們翻譯成函數(shù),這種就叫做
操作符函數(shù)(運(yùn)算符函數(shù))。
????編譯器把運(yùn)算翻譯成運(yùn)算符函數(shù),可以針對(duì)自定義的類(lèi)類(lèi)型設(shè)計(jì)它獨(dú)有的運(yùn)算功能。
????其實(shí)各種運(yùn)算符已經(jīng)具備一些功能,再次實(shí)現(xiàn)它的就是叫作運(yùn)算符重載。
????雙目運(yùn)算符:
????????a+b
????????成員函數(shù)
????????????a.operator+(b);
????????全局函數(shù)
????????????operator+(a,b);
????單目運(yùn)算符:
????????!a
????????成員函數(shù)
????????????a.operator!(void);
????????全局函數(shù)
????????????operator!(a);
二、雙目操作符函數(shù)重載
????成員函數(shù):
????const?類(lèi)對(duì)象?operator#(const?類(lèi)&?that)?const
????{
????????return?類(lèi)(參數(shù)#參數(shù));
????}
????注意:雙目錄運(yùn)算符的運(yùn)算結(jié)果是個(gè)右值,返回值應(yīng)該加?const?,然后為了const對(duì)象能夠調(diào)用,參數(shù)應(yīng)寫(xiě)const,函數(shù)也應(yīng)該具備const屬性。
????全局函數(shù):
????const?類(lèi)?operator#(const?類(lèi)&?a,const?類(lèi)&?b)
????{
????}
????注意:全局函數(shù)不是成員函數(shù),可能會(huì)需要訪(fǎng)問(wèn)到類(lèi)的私有成員,解決這種問(wèn)題可以把函數(shù)聲明為類(lèi)的友元函數(shù)(友元不是成員)。
????友元:在類(lèi)的外部想訪(fǎng)問(wèn)類(lèi)的私有成員(public/protected/private)時(shí),需要把所在的函數(shù)聲明為友元,但是友元只是朋友,因此它只有訪(fǎng)
問(wèn)權(quán),沒(méi)有實(shí)際的擁有權(quán)(其根本原因是它沒(méi)有this指針)。
????友元聲明:把函數(shù)的聲明寫(xiě)一份到類(lèi)中,然后在聲明前加上friend?關(guān)鍵字。使用友元即可把操作符函數(shù)定義為全局的,也可以確保類(lèi)的封裝性。
????注意:友元函數(shù)與成員函數(shù)不會(huì)構(gòu)成重載關(guān)系,因此它們不在同一個(gè)作用域內(nèi)。
三、賦值類(lèi)型的雙目操作符
????成員
????????類(lèi)?operator#(void)
????????{
????????????
????????}
????全局
????????類(lèi)?operator#(const?類(lèi)&?that)
????????{
????????}
????1、獲取單參構(gòu)造成賦值運(yùn)算的調(diào)用方式。
????String?str?=?"xxx";?//?會(huì)調(diào)用單參構(gòu)造,而不調(diào)用賦值運(yùn)算符
????str?=?"hhh";
????2、左操作數(shù)據(jù)不能具有const屬性
????????1.成員函數(shù)不能是常函數(shù)
????????2.全局函數(shù)第一個(gè)參數(shù)不能有const屬性
????3、返回值應(yīng)該都(成員/全局)具備const屬性
四、單目操作符函數(shù)重載
????-,~,!,&,*,->,++,--
????成員
????????const?類(lèi)?operator#(void)?const
????????{
????????????
????????}
????全局
????????const?類(lèi)?operator#(const?類(lèi)&?that)
????????{
????????}
????前++/--
????????類(lèi)&?operator#(void)
????????{
????????}
????????類(lèi)&?operator#(類(lèi)&?that)
????????{
????????}
????后++/--(啞元)
????????const?類(lèi)&?operator#(void)
????????{
????????}
????????const?類(lèi)&?operator#(類(lèi)&?that,int)
????????{
????????}
五、輸入輸出操作符重載
????cout?是?ostream?類(lèi)型的對(duì)象,cin?是?istream?類(lèi)型的對(duì)象。
????如果<</>>運(yùn)算實(shí)現(xiàn)為成員函數(shù),那么調(diào)用者應(yīng)該是ostream/istream,而我們無(wú)權(quán)增加標(biāo)準(zhǔn)庫(kù)的代碼,因此輸入/輸出運(yùn)算符只能定義為全局函數(shù)。
????ostream&?operator<<(ostream&?os,const?類(lèi)&?n)
????{
????}
????istream&?operator>>(istream&?os,類(lèi)&?n)
????{
????}
????注意:在輸入輸出過(guò)程中,cin/cout會(huì)記錄錯(cuò)誤標(biāo)志,因此不能加const屬性。
六、特殊操作符的重載(筆試面試比較重要)
????1、下標(biāo)操作符?[],常用于在容器類(lèi)型中以下標(biāo)方式獲取元素。
????類(lèi)型&?operator[](int?i)
????{
????}
????2、函數(shù)操作符(),一個(gè)類(lèi)如果重載函數(shù)操作符,那么它的對(duì)象就可以像函數(shù)一樣使用,參數(shù)的個(gè)數(shù)、返回值類(lèi)型,可以不確定,它是唯一一個(gè)
可以參數(shù)有缺省參數(shù)的操作符。
class?Array
{
????int*?arr;
????size_t?len;
public:
????Array(size_t?len):len(len)
????{
????????arr?=?new?int[len];
????}
????void?operator()(void)
????{
????????cout<<"emmm"<<endl;
????}
????int&?operator[](int?i)
????{
????????if(i?<?0?||?i?>=?len)
????????{
????????????cout<<"下標(biāo)錯(cuò)誤"<<endl;
????????????exit(0);
????????}
????????return?arr[i];
????}
};
int?main()
{
????Array?arr(100);
????for(int?i=0;?i<10;?i++)
????{
????????arr[i]?=?i;
????????cout<<?arr[i]?<<?endl;
????}
????arr();
}
????3、解引用操作符*,成員訪(fǎng)問(wèn)操作符->
????????如果一個(gè)類(lèi)重載了*和->,那么它的對(duì)象就可以像指針一樣使用。
????????所謂的智能指針就是一種類(lèi)對(duì)象,它支持解引用和成員訪(fǎng)問(wèn)操作符。
????4、智能指針
????????常規(guī)指針的缺點(diǎn):
????????????當(dāng)一個(gè)常規(guī)指針離開(kāi)它的作用域時(shí),只有該指針?biāo)加玫目臻g會(huì)被釋放,而它指向的內(nèi)存空間能否被釋放就不一定了,在一些特殊情
況(人為、業(yè)務(wù)邏輯特殊)free或delete沒(méi)有執(zhí)行,就會(huì)形成內(nèi)存泄漏。
????????智能指針的優(yōu)點(diǎn):
????????????智能指針是一個(gè)封裝了常規(guī)指針的類(lèi)類(lèi)型對(duì)象,當(dāng)它離開(kāi)作用域時(shí),它的析構(gòu)函數(shù)會(huì)自動(dòng)執(zhí)行,它的析構(gòu)函數(shù)會(huì)負(fù)責(zé)釋放常規(guī)指針?biāo)?/p>
指向的動(dòng)態(tài)內(nèi)存(以正確方式創(chuàng)建的智能指針,它的析構(gòu)函數(shù)才會(huì)正確執(zhí)行)。
????????智能指針和常規(guī)指針的相同點(diǎn):都支持*和->運(yùn)算。
????????智能指針和常規(guī)指針的不同點(diǎn):
????????????任何時(shí)候,一個(gè)對(duì)象只能使用一個(gè)智能指針來(lái)指向,而常規(guī)指針可以指向多次。
????????????只能指針的賦值操作需要經(jīng)過(guò)拷貝構(gòu)造和賦值構(gòu)造特殊處理(深拷貝)。
class?Int
{
public:
????int?val;
????Int(int?val=0):val(val){?}
????void?set_val(int?val)
????{
????????this->val?=?val;
????}
????int?get_val(void)
????{
????????return?val;
????}
????Int&?operator=(const?int?val)
????{
????????this->val?=?val;
????????return?*this;
????}
????~Int(void)
????{
????????cout<<"我是Int的析構(gòu)函數(shù)"<<endl;
????}
????friend?ostream&?operator>>(ostream&?os,Int&?n);
};
ostream&?operator<<(ostream&?os,Int&?n)
{
????return?os<<n.val;
}
class?IntPointer
{
????Int*?ptr;
public:
????IntPointer(Int*?ptr):ptr(ptr){?}
????Int&?operator*(void)
????{
????????return?*ptr;
????}
????~IntPointer(void)
????{
????????delete?ptr;
????}
};
int?main()
{
????Int*?num?=new?Int(100);
????IntPointer?p?=?num;
????*p?=?20;
????cout<<*p?<<endl;
????*p?=?10;
????cout<<*p?<<endl;
}
????????auto_ptr:標(biāo)準(zhǔn)庫(kù)中封裝好的智能指針,實(shí)現(xiàn)了常規(guī)指針的基本功能,頭文件?#include?<memory>
????????????用法:auto_ptr<指向的類(lèi)型>?指針變量名(對(duì)象的地址)
????????auto_ptr的局限性:
????????????不要使用?auto_ptr?對(duì)象保存指向動(dòng)態(tài)、靜態(tài)分配數(shù)組的指針。
????????????不能跨作用域使用,一旦離開(kāi)作用域指針變量會(huì)釋放它指向的對(duì)象也會(huì)釋放。
????????????不能放入標(biāo)準(zhǔn)容器。
????????????不能指向?qū)ο髷?shù)組。
#include?<memory>
class?A
{
public:
????A(void)
????{
????????cout<<"構(gòu)造"<<endl;
????}
????~A(void)
????{
????????cout<<"析構(gòu)"<<endl;
????}
????void?show(void)
????{
????????cout<<"A's?show"<<endl;
????}
};
int?main()
{
????auto_ptr<A>?ptr(new?A);
????(*ptr).show();
}
????5、new/delete/new[]/delete[]運(yùn)算符重載
????????1.C++缺省的堆內(nèi)存管理器速度較慢,重載new/delete底層使用malloc/free可以提高運(yùn)行速度。
????????2.new在失敗會(huì)產(chǎn)生異常,而每次使用new時(shí)為了安全都應(yīng)該進(jìn)行異常捕獲,而重載new操作符只需要在操作符函數(shù)中進(jìn)行一次錯(cuò)誤處理即可。
????????3.在一些占字節(jié)數(shù)比較小的類(lèi),頻繁使用new,可能會(huì)產(chǎn)生大量的內(nèi)存碎片,而重載new操作符后,可以適當(dāng)?shù)臄U(kuò)大每次申請(qǐng)的字節(jié)數(shù),減
少內(nèi)存碎片產(chǎn)生的機(jī)率。
????????4.重載?new/delete?可以記錄堆內(nèi)存使用的信息
????????5.重載?delete?可以檢查到釋放內(nèi)存失敗時(shí)的信息,檢查到內(nèi)存泄漏。
七、重載操作符的限制
????1、不能重載的操作符
????????域限定符?::
????????直接成員訪(fǎng)問(wèn)操作符?.
????????三目操作符??:
????????字節(jié)長(zhǎng)度操作符?sizeof
????????類(lèi)型信息操作符?typeid
????2、重載操作符不能修改操作符的優(yōu)先級(jí)
????3、無(wú)法重載所有基本類(lèi)型的操作符運(yùn)算
????4、不能修改操作符的參數(shù)個(gè)數(shù)
????5、不能發(fā)明新的操作符
關(guān)于操作符重載的建議:
????1、在重載操作符時(shí)要根據(jù)操作符實(shí)際的功能和意義來(lái)確定具體參數(shù),返回值,是否具有const屬性,返回值是否是引用或者臨時(shí)對(duì)象。
????2、重載操作符要符合情理(要有意義),要以實(shí)際用途為前提。
????3、重載操作符的意義是為了讓對(duì)象的操作更簡(jiǎn)單、方便,提高代碼的可讀性,而不是為了炫技。
????4、重載操作符要與默認(rèn)的操作符的功能、運(yùn)算規(guī)則一致,不要出現(xiàn)反人類(lèi)的操作。
????#define?ture?0
????#define?false?1
一、類(lèi)的繼承
????1、共性與個(gè)性
????????表達(dá)不同類(lèi)型事物之間公有的屬性和行為。
????????個(gè)性用于刻畫(huà)每種事物特有的屬性和行為。
????2、共性表示為父類(lèi)(基類(lèi)),個(gè)性表示為子類(lèi)(派生類(lèi))。
????????子類(lèi)繼承自父類(lèi)
????????基類(lèi)派生出子類(lèi)
二、繼承的基本語(yǔ)法
????1、繼承表
????一個(gè)子類(lèi)可以同時(shí)繼承零到多個(gè)父類(lèi),每個(gè)父類(lèi)的繼承方式可以相同也可以不同。
????class?子類(lèi):繼承方式1?父類(lèi)1,繼承方式2?父類(lèi)2,...
????{
????}
????2、繼承方式
????????public?公有繼承:父類(lèi)的特性可通過(guò)子類(lèi)向外擴(kuò)展。
????????private?私有繼承:父類(lèi)的特性只能為子類(lèi)所有。
????????protected?保護(hù)繼承:父類(lèi)的特性只能在繼承鏈內(nèi)擴(kuò)展。
三、繼承的基本特點(diǎn)
????1、公共特點(diǎn)(所有繼承都有的特點(diǎn))
????????子類(lèi)對(duì)象可以當(dāng)作父類(lèi)對(duì)象使用,子類(lèi)對(duì)象與父類(lèi)沒(méi)有本質(zhì)上的區(qū)別。
????????子類(lèi)的邏輯空間小于父類(lèi),但它的物理空間要大于等于父類(lèi)。
????????子類(lèi)對(duì)象?IS?A?父類(lèi)對(duì)象
????2、向上和向下轉(zhuǎn)換(造型)
????????從子類(lèi)到父類(lèi):子類(lèi)的指針或引用可以隱式轉(zhuǎn)換成父類(lèi)的指針或引用,這是一種縮小類(lèi)型的轉(zhuǎn)換,對(duì)于編譯器來(lái)說(shuō)是安全的。
????????從父類(lèi)到子類(lèi):父類(lèi)的指針或引用不可以轉(zhuǎn)換成子類(lèi)的指針或引用,這是一種擴(kuò)大類(lèi)型的轉(zhuǎn)換,在編譯器看來(lái)是危險(xiǎn)的。(子類(lèi)的指針指
向父類(lèi)的對(duì)象,不安全)
????????編譯器僅僅是檢查指針或引用的數(shù)據(jù)類(lèi)型,而對(duì)實(shí)際引用的目標(biāo)對(duì)象不關(guān)心(構(gòu)成多態(tài)的基礎(chǔ))。
????????類(lèi)型一致:父類(lèi)的指針或引用實(shí)際的目標(biāo)類(lèi)型是否需要轉(zhuǎn)換成實(shí)際的指針或引用由程序自己決定。
????3、子類(lèi)會(huì)繼承父類(lèi)的所有成員(公有,私有,保護(hù))
????4、子類(lèi)會(huì)隱藏父類(lèi)的同名成員
????????1.可以通過(guò)域限定符?父類(lèi)::隱藏成員?進(jìn)行訪(fǎng)問(wèn)父類(lèi)中的隱藏成員
????????2.可以使用父類(lèi)的指針或引用來(lái)指向子類(lèi)對(duì)象,然后訪(fǎng)問(wèn)父類(lèi)中的隱藏成員。
????5、雖然子類(lèi)繼承所有父類(lèi)中的成員,但不能訪(fǎng)問(wèn)父類(lèi)中的私有成員。
四、繼承方式影響訪(fǎng)問(wèn)控制
略
