C++開發(fā)知識點總結(jié)

一、C語言基礎(chǔ)

1橙垢、struct 的內(nèi)存對齊和填充問題
其實只要記住一個概念和三個原則就可以了:

  • 一個概念:
    自然對齊:如果一個變量的內(nèi)存起始地址正好位于它長度的整數(shù)倍,就被稱做自然對齊伦糯。
    如果不自然對齊钢悲,會帶來CPU存取數(shù)據(jù)時的性能損失。(PS:具體應(yīng)該與CPU通過總線讀寫內(nèi)存數(shù)據(jù)的細(xì)節(jié)相關(guān)舔株,具體沒有細(xì)究)
  • 三個原則:
    1)struct 的起始地址需要能夠被其成員中最寬的基本數(shù)據(jù)類型整除莺琳;
    2)struct 的size 也必須能夠被其成員中最寬的基本數(shù)據(jù)類型整除;
    3)struct 中每個成員地址相對于struct 的起始地址的offset载慈,必須是自然對齊的惭等。
    填充的字節(jié)是隨意填充任意字符的,除非賦值前先對結(jié)構(gòu)體變量s進行memset(s, 0, sizeof(s));進行整個內(nèi)存清零處理(全0填充办铡,也可以用其他字符全填充)辞做。

2、struct變量能進行比較嗎寡具?

  • C語言不能直接用==秤茅、>、>=童叠、<框喳、<=這些進行比較课幕。但是C++若是對這些用于比較的關(guān)系運算符進行了重載(重載內(nèi)部可以調(diào)用memcmp函數(shù)),那就可以進行比較了五垮。
  • 在對結(jié)構(gòu)體變量都用memset初始化的前提下乍惊,可以用memcmp函數(shù)進行一個一個字節(jié)的比較。int memcmp(const void *buf1, const void *buf2, unsigned int count);相同返回0放仗,大于返回正數(shù)润绎,小于返回負(fù)數(shù)。(類似于strcmp函數(shù)的返回值)
  • 若是沒有用memset統(tǒng)一初始化填充字節(jié)诞挨,那么任意的填充字節(jié)就會影響比較結(jié)果莉撇。
  • 結(jié)構(gòu)體變量之間可以直接賦值,如struct student stu1 = stu2;但是注意惶傻,賦值是調(diào)用memcpy一個一個字節(jié)復(fù)制過去的稼钩,而不是按照stu1.id=stu2.id;這樣一個一個成員復(fù)制過去的。因此對于用=直接賦值的兩個變量达罗,再用memcmp比較坝撑,肯定是相等的。

3粮揉、運算符優(yōu)先級問題

  • 優(yōu)先級排序:() > !非(其他單目的) > 算術(shù)運算符 > 關(guān)系(> < == !=)運算符>邏輯(& ^ | && ||)運算符>賦值運算符>逗號

https://blog.csdn.net/u013630349/article/details/47444939

4巡李、手寫strcpy字符串拷貝函數(shù)?先問要C風(fēng)格的還是C++風(fēng)格的扶认?

#include <assert.h>
#include <stdio.h>
char* strcpy(char* des, const char* src)  //注意const侨拦。C++風(fēng)格的(char& des, const char &src)
{
    assert((des!=NULL) && (src!=NULL));   //注意輸入合法性檢查。
//但是對于C++可以有更好的解決方案辐宾,將輸入?yún)?shù)指針改為引用&狱从,就默認(rèn)不能傳入NULL,否則報錯
    char *address = des;          //注意返回值是目的串首地址
    while((*des++ = *src++) != '\0')  
        ;  
    return address;
}

https://www.nowcoder.com/ta/review-c/review?tpId=22&tqId=21053&query=&asc=true&order=&page=4
另有strlen叠纹、strcmp季研、strcat函數(shù)的手寫:https://blog.csdn.net/lisonglisonglisong/article/details/44278013
另外strncpy函數(shù),復(fù)制完后des目標(biāo)字符串的末尾不會有'\0'結(jié)束符誉察,一種解決辦法是自己加上一行 des[n]='\0';与涡。更好的做法是給des字符數(shù)組初始化為全空字符串char des[100]="";或者memset(des, 0, 100);

5、手寫一個memcpy函數(shù)持偏?

  • 陷阱是要注意有重疊區(qū)的拷貝驼卖,比如str[10]="123456789",要memcpy(str+2, str, 5);鸿秆,若是不考慮重疊區(qū)而簡單的從前往后拷貝酌畜,會得到返回"1212189",然而我們想要的結(jié)果是"1234589"
void* Memcpy(void* dest, const void* src, size_t size)
{
    char *pdest, *psrc;
    if (NULL == dest || NULL == src)
    {
        return NULL;
    }
    //要考慮如果目標(biāo)地址和源地址后面有重疊卿叽,則要防止源地址后面的原始數(shù)據(jù)被目標(biāo)地址拷貝過來的數(shù)據(jù)覆蓋
    //這種情況就需要從后往前拷貝
    if (dest>src && (char *)dest<(char *)src+size)
    {
        pdest = (char *)dest + size - 1;
        psrc = (char *)src + size - 1;
        while (size--)
        {
            *pdest-- = *psrc--;
        }
    }
    //從前往后正城虐拷貝賦值
    else
    {
        pdest = (char*)dest;
        psrc = (char*)src;
        while (size--)
        {
            *pdest++ = *psrc++;
        }
    }
    return dest;
}

6恳守、main函數(shù)的返回值作用?如何捕獲返回值埠戳?

  • int main(int argc, char *argv[]) { return 0; }標(biāo)準(zhǔn)main函數(shù)寫法,在命令行運行可執(zhí)行文件時蕉扮,argc表示參數(shù)個數(shù)整胃,argv表示指針數(shù)組旧噪,數(shù)組里面每個元素都是一個字符串指針首地址淆九。
  • 返回0表示程序正常退出,返回其他數(shù)字的含義由操作系統(tǒng)決定匆瓜。
  • 在windows中再命令行執(zhí)行上面最簡單的代碼編譯承德可執(zhí)行文件test.exe后奔则,再執(zhí)行命令echo %ERRORLEVEL%(windows命令行大小寫不敏感)可以輸出main函數(shù)的返回值:0蛮寂,修改為return -1;實驗后也可以看到返回值為:-1。
  • 在linux中可在執(zhí)行可執(zhí)行文件./test后易茬,再輸入一行命令echo $?打印出main函數(shù)的返回值酬蹋。
  • 在win的test.exe && dir或linux的./test && ls,都可以根據(jù)可執(zhí)行文件的main返回值來判斷是否后面的系統(tǒng)命令是否繼續(xù)執(zhí)行抽莱,main返回值為0時才表示真范抓,則后面的列出當(dāng)前目錄命令都可以執(zhí)行。main返回其他值則后面的不繼續(xù)執(zhí)行食铐。

7匕垫、進程間通信的方式有哪些?

  • 1虐呻、無名管道(父子進程兄弟進程使用int pipe(int fd[2]);fd[0]單收fd[1]單發(fā))象泵;
    2、有名管道FIFO(任意兩進程間斟叼,是一種文件類型mkfifo("fifo1", 0666);偶惠,文件標(biāo)識符fd = open("fifo1", O_WRONLY));管道的實質(zhì)是一個內(nèi)核緩沖區(qū)朗涩,管道一端的進程順序地將進程數(shù)據(jù)寫入緩沖區(qū)洲鸠,另一端的進程則順序地讀取數(shù)據(jù)。
    3馋缅、共享內(nèi)存(key = ftok(".", 'z')扒腕,創(chuàng)建并獲取共享內(nèi)存號shmid = shmget(key, 1024, IPC_CREAT|0666));采用共享內(nèi)存進行通信的一個主要好處是效率高萤悴,因為進程可以直接讀寫內(nèi)存瘾腰,而不需要任何數(shù)據(jù)的拷貝,對于像管道和消息隊列等通信方式覆履,則需要再內(nèi)核和用戶空間進行四次的數(shù)據(jù)拷貝蹋盆,而共享內(nèi)存則只拷貝兩次:一次從輸入進程到共享內(nèi)存區(qū)费薄,另一次從共享內(nèi)存到輸出進程。
    4栖雾、消息隊列(需要內(nèi)核維護這個消息隊列key = ftok("/etc/passwd",'z')楞抡,隊列號msqid = msgget(key, IPC_CREAT|0777));消息隊列克服了信號傳遞信息少析藕、管道只能承載無格式字節(jié)流以及緩沖區(qū)大小受限等缺點召廷。消息隊列,就是一個消息的鏈表账胧,是一系列保存在內(nèi)核中消息的列表竞慢。用戶進程通過獲取消息隊列的唯一ID可以向消息隊列添加消息,也可以向消息隊列讀取消息治泥。
    5筹煮、信號(如一些引發(fā)中斷信號kill);幾十種信號和以他們的名稱命名的常量居夹,通常程序中直接包含<signal.h>就好败潦。信號是在軟件層次上對中斷機制的一種模擬,是一種異步通信方式准脂,信號常用于用戶進程和內(nèi)核之間直接交互变屁。內(nèi)核也可以利用信號來通知用戶空間的進程來通知用戶空間發(fā)生了哪些系統(tǒng)事件。信號事件有兩個來源:1)硬件來源意狠,例如按下了cltr+C粟关,通常產(chǎn)生中斷信號sigint。2)軟件來源环戈,例如使用系統(tǒng)調(diào)用或者命令發(fā)出信號闷板。最常用的發(fā)送信號的系統(tǒng)函數(shù)是kill,raise函數(shù)。軟件來源還包括一些非法運算等操作院塞。
    6遮晚、信號量(也是需要由內(nèi)核維護P獲取信號量操作減一,V釋放信號量操作加1拦止,只是同步計數(shù)器不傳數(shù)據(jù)key = ftok(".", 'z')县遣,獲取信號量idsem_id = semget(key, 1, IPC_CREAT|0666));
    7汹族、socket(可跨網(wǎng)絡(luò)主機進程通信)sys/socket.h 或 winsock2.h
    http://www.reibang.com/p/21fba1542026
  • socket網(wǎng)絡(luò)跨主機的進程間通信是以五元組【源IP萧求,源端口,目的IP顶瞒,目的端口夸政,TCP或UDP協(xié)議】,來找到不同的進程榴徐,并互發(fā)數(shù)據(jù)進行通信守问。
  • 同一主機上的進程間通信匀归,大都是使用在本主機上唯一的key關(guān)鍵值來標(biāo)識一個可供不同進程訪問的資源,這個唯一的關(guān)鍵值又大都是與某一個實際存在的文件綁定耗帕,利用ftok()函數(shù)key_t ftok( const char * fname, int id );穆端,可根據(jù)fname即某文件名(一般是系統(tǒng)上一定存在的文件)找到它的索引節(jié)點號(唯一標(biāo)識一個文件的node_id)搭配提供的另一個參數(shù)id得到唯一的key值=(16進制的)id連接node_id。因此不同的進程只要都遵守這個約定提供相同的參數(shù)給ftok()函數(shù)就都可以獲取到這個唯一的key_id仿便,然后根據(jù)key_id再獲取到對應(yīng)的進程間通信方式的id號体啰,然后對其進行讀寫數(shù)據(jù)操作。
    類似的IPC有FIFO命名管道探越、消息隊列(前面兩個自帶進程同步效果)狡赐,信號量(只能控制進程同步窑业,要想互發(fā)數(shù)據(jù)需要結(jié)合共享內(nèi)存)钦幔、共享內(nèi)存
  • 無名管道由于只在父子進程或兄弟進程間通信,可以在主進程中通過pipe(fd)隨機產(chǎn)生出管道的文件描述符常柄。不需要在主機上唯一鲤氢,因為只在這幾個相關(guān)的進程中私有。

8西潘、線程間的通信

  • 共享的全局變量卷玉,然后利用全局的鎖變量或者信號量來實現(xiàn)同步互斥訪問共享的全局變量達(dá)到通信的目的。
  • 消息傳遞(如窗口應(yīng)用的點擊按鈕事件傳遞)喷市、事件訂閱機制等

9相种、進程調(diào)度算法有哪些?Linux使用的什么進程調(diào)度方法品姓?

  • 【解】進程調(diào)度算法有下面幾種:
    先來先服務(wù)
    短作業(yè)優(yōu)先
    時間片輪轉(zhuǎn)
    基于優(yōu)先級

Linux系統(tǒng)中寝并,進程分為實時和非實時兩種:

  • 實時進程(相對于普通進程優(yōu)先級更高)
    SCHED_FIFO —> 相同優(yōu)先級時,先來先服務(wù)腹备;不同優(yōu)先級衬潦,搶占式調(diào)度。進程一旦占用cpu則一直運行植酥,直到有更高優(yōu)先級任務(wù)到達(dá)或自己放棄镀岛。
    SCHED_RR —> 時間片輪轉(zhuǎn),搶占式調(diào)度友驮。
  • 普通進程
    SCHED_OTHER —> priority(靜態(tài)優(yōu)先級)+counter(剩余時間片)之和作為動態(tài)優(yōu)先級漂羊,基于動態(tài)優(yōu)先級的時間片輪轉(zhuǎn)。

10卸留、虛擬內(nèi)存和物理內(nèi)存拨与?

  • 先說說為什么會有虛擬內(nèi)存和物理內(nèi)存的區(qū)別。正在運行的一個進程艾猜,他所需的內(nèi)存是有可能大于內(nèi)存條容量之和的买喧,比如你的內(nèi)存條是256M捻悯,你的程序卻要創(chuàng)建一個2G的數(shù)據(jù)區(qū),那么不是所有數(shù)據(jù)都能一起加載到內(nèi)存(物理內(nèi)存)中淤毛,勢必有一部分?jǐn)?shù)據(jù)要放到其他介質(zhì)中(比如硬盤)今缚,待進程需要訪問那部分?jǐn)?shù)據(jù)時,再通過調(diào)度從磁盤進入物理內(nèi)存低淡。所以姓言,虛擬內(nèi)存是進程運行時所有內(nèi)存空間的總和,并且可能有一部分不在物理內(nèi)存中而在磁盤中蔗蹋,而物理內(nèi)存就是我們平時所了解的內(nèi)存條何荚。有的地方呢,也叫這個虛擬內(nèi)存為內(nèi)存交換區(qū)猪杭。

1)每個進程都有自己的虛擬內(nèi)存空間餐塘,所有進程共享物理內(nèi)存空間。
2)虛擬內(nèi)存大小和機器的位數(shù)有關(guān)皂吮,如32位的是4GB的地址總線(0~0xFFFFFFFF字節(jié))其中每個進程都有自己的0~0xBFFFFFFF的3GB虛擬內(nèi)存空間——用戶空間戒傻,然后所有進程共用0xCFFFFFFF~0xFFFFFFFF這1GB的虛擬內(nèi)存空間——內(nèi)核空間。64位的有2^64字節(jié)的地址總線蜂筹。


3)該圖顯示了兩個 64 位進程的虛擬地址空間:Notepad.exe 和 MyApp.exe需纳。每個進程都有其各自的虛擬地址空間,范圍從 0x000'0000000 至 0x7FF'FFFFFFFF(8TB用戶進程虛擬內(nèi)存空間)艺挪。每個陰影框都表示虛擬內(nèi)存或物理內(nèi)存的一個頁面(大小都為4KB)不翩。注意,Notepad 進程使用從 0x7F7'93950000 開始的虛擬地址的三個相鄰頁面麻裳。但虛擬地址的這三個相鄰頁面會映射到物理內(nèi)存中的非相鄰頁面口蝠。而且還注意,兩個進程都使用從 0x7F7'93950000 開始的虛擬內(nèi)存頁面掂器,但這些虛擬頁面都映射到物理內(nèi)存的不同頁面亚皂,說明個進程間的虛擬內(nèi)存互不影響。
4)這種虛擬內(nèi)存一個頁page(4KB)和物理內(nèi)存一個頁幀(4KB)之間的映射關(guān)系国瓮,由操作系統(tǒng)的一個頁表來維護灭必,頁表中給各個頁都有編號(頁號和頁幀號對應(yīng)映射)。進程中虛擬內(nèi)存頁》頁表中的頁號—MMU內(nèi)存管理單元—頁表中的頁幀號《物理內(nèi)存中的頁幀【由MMU的頁表來維護映射關(guān)系】

5)但是問題來了乃摹,虛擬內(nèi)存頁的個數(shù)=3GB/4KB > 物理內(nèi)存頁幀的個數(shù)=256MB/4KB禁漓,豈不是有些虛擬內(nèi)存頁的地址永遠(yuǎn)沒有對應(yīng)的物理內(nèi)存地址空間?不是的孵睬,操作系統(tǒng)是這樣處理的播歼。操作系統(tǒng)有個缺頁中斷(page fault)缺頁異常功能。操作系統(tǒng)把暫時不訪問的物理內(nèi)存頁幀,讓他缺頁失效秘狞,并把它的原內(nèi)容寫入磁盤轉(zhuǎn)存起來——這個過程叫分頁-分頁是磁盤和內(nèi)存間傳輸數(shù)據(jù)塊的最小單位叭莫。,隨后把當(dāng)前需要訪問的頁放到頁幀中烁试,并修改頁表中的映射雇初,這樣就保證所有的頁都有被調(diào)度的可能了。當(dāng)進程又要訪問原轉(zhuǎn)存的內(nèi)容减响,會先訪問原物理內(nèi)存頁但是引發(fā)缺頁中斷然后從磁盤中取出頁內(nèi)容到物理內(nèi)存頁靖诗,然后進程訪問到數(shù)據(jù)繼續(xù)執(zhí)行。這就是處理虛擬內(nèi)存地址到物理內(nèi)存的步驟支示。

https://www.cnblogs.com/curtful/archive/2012/02/16/2354496.html

11刊橘、為什么需要虛擬內(nèi)存?通過虛擬地址訪問內(nèi)存有哪些優(yōu)勢颂鸿?

  • 虛擬內(nèi)存地址可以提供只讀只寫的訪問權(quán)限來保護實際對應(yīng)的物理地址的數(shù)據(jù)促绵。
  • 程序可以使用一系列相鄰的虛擬地址來訪問物理內(nèi)存中不相鄰的大內(nèi)存緩沖區(qū)。
  • 不同進程使用的虛擬地址彼此隔離据途。一個進程中的代碼無法更改正在由另一進程或操作系統(tǒng)使用的物理內(nèi)存绞愚。
  • 程序可以使用一系列虛擬地址來訪問大于可用物理內(nèi)存的內(nèi)存緩沖區(qū)叙甸。當(dāng)物理內(nèi)存的供應(yīng)量變小時颖医,內(nèi)存管理器會將(暫時不用的)物理內(nèi)存頁(通常大小為 4 KB)保存到磁盤文件,從而空出可用的物理內(nèi)存裆蒸。數(shù)據(jù)或代碼頁會根據(jù)需要在物理內(nèi)存與磁盤之間移動熔萧。

12、死鎖產(chǎn)生的四個條件僚祷,死鎖發(fā)生后怎么檢測和恢復(fù)佛致?

  • 死鎖的四個必要條件:
    互斥條件:一個資源每次只能被一個進程使用。
    請求與保持條件:一個進程在申請新的資源的同時保持對原有資源的占有辙谜。
    不剝奪條件:進程已獲得的資源俺榆,在未使用完之前,不能強行剝奪装哆。
    循環(huán)等待條件:若干進程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系罐脊。
  • 死鎖發(fā)生后:
    檢測死鎖:首先為每個進程和每個資源指定一個唯一的號碼,然后建立資源分配表和進程等待表蜕琴。
    解除死鎖:當(dāng)發(fā)現(xiàn)有進程死鎖后萍桌,可以直接撤消死鎖的進程或撤消代價最小的進程,直至有足夠的資源可用凌简,死鎖狀態(tài)消除為止上炎。

13、不用中間變量實現(xiàn)兩個元素交換值雏搂。

void swap1(int &a, int &b)
{  //異或操作只適用于整型數(shù)藕施,不能用于小數(shù)
    if (a != b)  //如果兩個數(shù)地址相同寇损,異或操作會清零
    {
        a ^= b;
        b ^= a;
        a ^= b;
    }
}
void swap2(double  &a, double &b)
{//用double型可以使適用的范圍更大,防止加法和越界
    a = a + b;
    b = a - b;
    a = a - b;
}

14裳食、一些常見的位操作

  • 位操作符的運算優(yōu)先級比加減更低润绵,如int a = 1 << 2+ 1;得到a的值1左移3位得8,與int a = (1 << 2)+ 1;得5不同胞谈。
  • 取得a的相反數(shù)-a:~a+1; a取反再加一尘盼。
  • 可以用if ((a & 1) == 0)代替if (a % 2 == 0)來判斷a是不是偶數(shù)。即只需判斷a的最低位是0還是1烦绳。
  • 取得絕對值
int my_abs(int a)
{
    int i = a >> 31;  //得到最高位符號位卿捎,a為正或0返回0,a為負(fù)數(shù)返回-1
    return i == 0 ? a : (~a + 1);  //正數(shù)返回本身径密,負(fù)數(shù)返回相反數(shù)
    //return (a^i)-i;  //a^0 ==a本身午阵;a^(-1) == a取反,再減-1即加1得到相反數(shù)
}
  • 給出一個16位的無符號整數(shù)享扔。稱這個二進制數(shù)的前8位為“高位”底桂,后8位為“低位”。現(xiàn)在寫一程序?qū)⑺母叩臀唤粨Q惧眠。a = (a >> 8) | (a << 8);

15籽懦、守護、僵尸氛魁、孤兒進程的概念暮顺?

  • 守護進程:運行在后臺的一種特殊進程,獨立于控制終端并周期性地執(zhí)行某些任務(wù)秀存。如何實現(xiàn)守護進程捶码?(孤兒進程)
    僵尸進程:一個進程 fork 子進程,子進程退出或链,而父進程沒有 wait/waitpid子進程惫恼,那么子進程的進程描述符仍保存在系統(tǒng)中,這樣的進程稱為僵尸進程澳盐。
    孤兒進程:一個父進程退出祈纯,而它的一個或多個子進程還在運行,這些子進程稱為孤兒進程洞就。(孤兒進程將由 init 進程(進程號pid為1)收養(yǎng)并對它們完成狀態(tài)收集工作)

16盆繁、一個進程的主線程結(jié)束后,其他線程怎么辦旬蟋?

  • 通常一個進程的主線程是指的main()函數(shù)啟動后的那個線程油昂。實際上一個進程的所有線程是平等的,沒有主線程子線程之分。
  • 如果讓main()函數(shù)執(zhí)行到最后一行(默認(rèn)return 0;)或return其他值來結(jié)束主線程冕碟,然后調(diào)用glibc庫函數(shù)exit拦惋,exit進行相關(guān)清理工作后調(diào)用_exit系統(tǒng)調(diào)用退出該進程。因為進程結(jié)束了安寺,所以該進程所有的線程也就結(jié)束了厕妖。
  • 如果main()主線程在沒有運行到return退出前就由pthread_cancel(tid);終止了,那么就不會引發(fā)進程結(jié)束挑庶,其他的線程可以繼續(xù)正常運行言秸。而所謂的線程之間共享的全局變量是在main()函數(shù)之外也就是主線程之外的,因此主線程main函數(shù)內(nèi)的局部變量銷毀對其他線程也無影響迎捺。
  • 如果系統(tǒng)調(diào)用#kill 某線程號 举畸,也是可以結(jié)束整個進程的。

17凳枝、內(nèi)存泄漏如何避免抄沮?發(fā)生后如何檢查定位?

  • 內(nèi)存泄漏是指用malloc/realloc/new申請的堆內(nèi)存岖瑰,在銷毀指向這些堆內(nèi)存的指針之前沒有調(diào)用對應(yīng)的free/delete歸還內(nèi)存給堆叛买。使得這些堆內(nèi)存不能再被利用。然而不管內(nèi)存泄漏多么輕微蹋订,當(dāng)程序長時間運行時率挣,其破壞力是驚人的 - 從性能下降到內(nèi)存耗盡,甚至?xí)绊懫渌绦虻恼_\行辅辩。
  • 內(nèi)存泄漏的避免:一是從編程習(xí)慣來談要求我們記得在同一個代碼塊成對使用malloc/free或new/delete或new[]數(shù)組/delete[]數(shù)組难礼,最好不要在函數(shù)內(nèi)申請內(nèi)存然后返回指針娃圆,這樣在調(diào)用函數(shù)后很容易忘記這個指針指向的內(nèi)存是堆內(nèi)存而忘記手動釋放玫锋;而是應(yīng)該在函數(shù)外申請內(nèi)存,然后將指針作為參數(shù)傳遞給函數(shù)使用讼呢,這樣在函數(shù)使用完后我們還可以從上面代碼看到這個指針是指向堆內(nèi)存的而輕松手動釋放撩鹿。二是在C++中可以使用智能指針,使得在指向堆內(nèi)存的指針在用完后會隨著智能指針的析構(gòu)而調(diào)用free/delete函數(shù)釋放該指針指向的內(nèi)存悦屏,就不需要手動顯示地去釋放节沦。三是使用內(nèi)存池去集中申請堆內(nèi)存,然后集中釋放堆內(nèi)存础爬。四是別忘了將基類的析構(gòu)函數(shù)定義為虛函數(shù)甫贯,否則在多態(tài)使用父類指針指向子類對象時,析構(gòu)的時候會無法調(diào)用子類的析構(gòu)函數(shù)釋放子類的資源看蚜,從而造成泄漏叫搁。
  • 內(nèi)存泄漏發(fā)生后的定位:一是先找到發(fā)生內(nèi)存泄漏的程序在linux下可以用top命令列出正在運行的程序占用的內(nèi)存,windows下可以查看任務(wù)管理器,從中分析出異常的程序渴逻,然后通過#ps aux| grep 程序名》得到進程號pid》然后#kill pid結(jié)束這個進程疾党;二是尋找程序中引發(fā)內(nèi)存泄漏的代碼,若是代碼量不大惨奕,可以通過人工分析是否malloc/new對應(yīng)的指針都得到了正確的釋放雪位,然后重點檢查程序會循環(huán)執(zhí)行的那些代碼就是引起內(nèi)存泄漏一直增長的地方。如果還是找不到梨撞,那就需要借助一些工具來定位雹洗,比如使用vs編程可以安裝一個VLD(Visual Leak Detector)插件,使用的時候#include <vld.h>就可以了調(diào)試程序看到代碼檢查的報錯結(jié)果卧波。在linux下可以使用 Valgrind Memcheck工具的使用方式如下:valgrind --tool=memcheck ./a.out其中a.out為要檢查的程序編譯生成的可執(zhí)行文件(為了使valgrind發(fā)現(xiàn)的錯誤更精確队伟,能夠定位到源代碼行,建議在編譯時加上-g參數(shù)即加入gdb調(diào)試信息)幽勒。

二嗜侮、C++基礎(chǔ)

1、new表達(dá)式(new operator)和std::operator new標(biāo)準(zhǔn)庫函數(shù)的區(qū)別

  • new operator 是先調(diào)用operator new函數(shù)來分配返回值為void*的內(nèi)存啥容,然后再調(diào)用作用類型的構(gòu)造函數(shù)去初始化賦值這塊內(nèi)存锈颗。如string * str = new string("hello");就是對new表達(dá)式的常用方式。

這里我們可以這么理解咪惠,new表達(dá)式(new operator)其實可以分解為兩部击吱,即先調(diào)用new操作符(operator new)申請內(nèi)存,再調(diào)用placement new來初始化對象遥昧。即上面對new表達(dá)式的使用string * str = new string("hello");等價于下面兩句覆醇。

void *buffer = ::operator new(sizeof(string));
buffer = new(buffer) string(“hello”);
  • 而operator new函數(shù)相當(dāng)于是malloc,其實它的內(nèi)部實現(xiàn)也就是調(diào)用了void * malloc(size_t size);函數(shù)炭臭。
 void* operator new(size_t sizt)
{
      return malloc(size);      
}

詳見:http://www.reibang.com/p/a07ba8f384da

2永脓、new和malloc有哪些區(qū)別?


最重要的是要說到最下面一條鞋仍。
詳見:https://www.cnblogs.com/QG-whz/p/5140930.html

3常摧、頭文件防衛(wèi)式編程,防止頭文件多次嵌套導(dǎo)入威创。
比如要編寫頭文件mystring.h落午,就可以這樣寫。如果統(tǒng)一遵循這種規(guī)則肚豺,那么就不會出現(xiàn)mystring.h頭文件內(nèi)容在編譯前預(yù)處理時被重復(fù)導(dǎo)入的情況溃斋。

#ifndef __MYSTRING__
#define __MYSTRING__

#include<string.h>    //用到C語言的字符串處理函數(shù)
class mystring
{
public:
        mystring();
        ~mystring();
private:
        char * head;
        int length;
};
#endif

4吸申、const函數(shù)梗劫?——函數(shù)定義后面加const有什么作用寞奸?

  • 只有類成員函數(shù)后面才能加const郁岩,普通函數(shù)沒有所謂的const函數(shù)耀石,不能在后面加const颁井。如復(fù)數(shù)complex類的獲取實部成員函數(shù)int getReal() const { return real; }
  • 作用是表示此類成員函數(shù)不能改變所有類成員變量的值竖独。
  • 當(dāng)定義一個const的類對象時const complex con1;奇徒,此對象con1就只能調(diào)用這些const成員函數(shù)干像,調(diào)用非const成員函數(shù)就會報錯赖歌。

5构资、C++內(nèi)存分區(qū)割坠?進程虛擬內(nèi)存分布齐帚?共有5個分區(qū)

6、一個進程可用的內(nèi)存大小彼哼、線程可用內(nèi)存大卸酝?堆內(nèi)存多大敢朱?棧內(nèi)存多大剪菱?

  • 根據(jù)上面這張圖可以分析出,一個用戶進程最多可用3GB內(nèi)存拴签,進程里既有堆又有棧孝常。而一個線程只有自己的棧空間蚓哩,所以可用的最大棧內(nèi)存也就是上圖說的8MB或10MB(可手動修改)构灸。因此一個最多可開辟3GB/10MB=300個線程。
  • 對于4GB內(nèi)存封頂?shù)?2位系統(tǒng)岸梨,用戶空間堆內(nèi)存<3GB(留1GB給內(nèi)核空間)喜颁,而棧內(nèi)存一般只有8M(ubuntu)或10M(centos)各系統(tǒng)默認(rèn)值不同,可修改曹阔。

7半开、用vs編程時debug和release版本有啥區(qū)別?

  • Debug通常稱為調(diào)試版本次兆,它包含很多調(diào)試信息稿茉,并且不作任何優(yōu)化,便于程序員調(diào)試程序(加斷點實時查看變量值)芥炭。會檢查assert斷言語句。也可關(guān)閉assert斷言——先#define NDEBUG#include<assert.h>恃慧,就可以將后面寫的那些assert斷言語句關(guān)閉檢查园蝠。
  • Release稱為發(fā)布版本,它往往是進行了各種優(yōu)化(比如調(diào)整某些變量在棧內(nèi)存的地址順序)痢士,使得程序在代碼大小和運行速度上都是最優(yōu)的彪薛,以便用戶很好的使用茂装。會忽略assert斷言語句。

8善延、面向?qū)ο蟮乃拇筇匦灾v一下少态?

  • 抽象:將多種事物抽象出共同特征形成一個抽象父類。
  • 封裝:將變量和對變量的操作組合在一起形成一個類或者一個模塊易遣,就是封裝彼妻。一處封裝,多處調(diào)用豆茫。目的是代碼復(fù)用侨歉。
  • 繼承:子類在原有父類代碼的基礎(chǔ)上進行擴展。目的是代碼復(fù)用揩魂。
  • 多態(tài):調(diào)用同名函數(shù)幽邓,卻因為上下文環(huán)境不同,而會有不同的實現(xiàn)火脉。一個函數(shù)接口多種實現(xiàn)牵舵。目的是接口復(fù)用。

9倦挂、C++多態(tài)分幾類棋枕?多態(tài)有幾種實現(xiàn)方式?

10妒峦、C能直接用C++寫的動態(tài)庫嗎重斑?C++能直接用C寫的動態(tài)庫嗎?

  • 都不能直接互用肯骇。主要原因是C++有函數(shù)重載窥浪,相同函數(shù)在匯編代碼中的會帶上參數(shù)表示,而C不支持函數(shù)重載因此在匯編中函數(shù)名只有函數(shù)名笛丙。比如int add(int a, int b);漾脂,在C++編譯后的匯編代碼中是_add_int_int或其他表示,而在C語言匯編代碼中只是_add胚鸯。
  • 因此骨稿,C++要調(diào)用用C的動態(tài)庫,不用重新編譯C姜钳,只需在自己的C++代碼中吧要調(diào)用的C函數(shù)原型用extern "C" int add(int a, int b);坦冠,這樣該C++代碼在編譯是就會將這個add函數(shù)原型用C的形式編譯成_add。然后就和原C的動態(tài)庫函數(shù)名對上了哥桥。否則C++代碼中編譯成_add_int_int在調(diào)用C動態(tài)庫時會報錯找不到該函數(shù)辙浑。
  • C要調(diào)用C++的動態(tài)庫時,必須修改原C++代碼拟糕,將要被C調(diào)用的函數(shù)原型修改為extern "C" int add(int a, int b);判呕,就可以被編譯成C形式了倦踢,然后重新編譯就可供C調(diào)用。編譯完成后侠草,提供給c使用的頭文件里面不能包含extern “C”辱挥,可以使用宏開關(guān)解決,也可以重新寫個頭文件边涕。當(dāng)然還有一點就是該函數(shù)內(nèi)部不能包含用C++特有的東西晤碘。
  • 總結(jié),要想互相能調(diào)用奥吩,都只需要對C++代碼進行修改哼蛆,函數(shù)原型前加extern "C",而C代碼不需要修改霞赫。

11腮介、從源代碼到可執(zhí)行文件經(jīng)歷了那幾步?

  • 文本源代碼》預(yù)處理(得去注釋端衰、宏替換叠洗、展開頭文件等的文本源碼》編譯(得文本匯編代碼.S)》匯編(得二進制的.o目標(biāo)文件)》鏈接多個.o(得到二進制可執(zhí)行文件)

12、C和C++的區(qū)別

  • C語言更加注重的是過程旅东,C++除了保留C語言的優(yōu)點灭抑,還增加了面向?qū)ο蟮臋C制。
  • 應(yīng)用場景來看抵代,由于C沒有面向?qū)ο筇诮冢栽诔绦蛞?guī)模太大的時候?qū)懫饋砗軓?fù)雜,面向?qū)ο蟮腃++更適合大規(guī)模的程序荤牍。
  • C沒類class面向?qū)ο蟾嗟氖敲嫦蜻^程編程案腺,struct的用法也有差異,比如定義struct變量的時候C要帶上struct關(guān)鍵字康吵,而C++的struct用法和class一樣劈榨,只是內(nèi)部數(shù)據(jù)訪問權(quán)限struct默認(rèn)public,calss默認(rèn)private晦嵌。
  • C沒有bool型同辣,可用int型代替。C沒有方便的string可用
  • C++的泛型編程和STL提供的多種容器也是C所沒有的惭载。
  • 函數(shù)重載旱函,操作符重載,C也沒有棕兼。這也導(dǎo)致兩者代碼不能直接調(diào)用陡舅。

13、C++和java的區(qū)別

  • 我覺得C++與Java最大的區(qū)別是在于內(nèi)存管理上伴挚,C++的內(nèi)存管理是需要程序員自己控制的靶衍,自己開了需要自己去釋放。然而Java提供了JVM茎芋,JVM就是用來進行內(nèi)存管理的颅眶,不需要程序員自己手動開關(guān)。
  • Java呢田弥,摒棄了C++很多復(fù)雜的特性涛酗,比如指針、多繼承偷厦、操作符重載等等商叹,相對來說Java的編程學(xué)習(xí)入門比較容易。

14只泼、static_cast 和 dynamic_cast 的區(qū)別剖笙?兩個操作符

  • cast轉(zhuǎn)換發(fā)生的時間不同,一個是static靜態(tài)編譯時请唱,一個是動態(tài)運行時弥咪;
  • static_cast是相當(dāng)于C的強制類型轉(zhuǎn)換,用起來可能有一點危險十绑,不提供運行時的檢查來確保轉(zhuǎn)換的安全性聚至。
  • dynamic_cast用于轉(zhuǎn)換指針和和引用,不能用來轉(zhuǎn)換對象 —— 主要用于類層次間的上行轉(zhuǎn)換和下行轉(zhuǎn)換本橙,還可以用于類之間的交叉轉(zhuǎn)換扳躬。在類層次間進行上行轉(zhuǎn)換時,dynamic_cast和static_cast的效果是一樣的甚亭;在進行下行轉(zhuǎn)換時贷币,dynamic_cast具有類型檢查的功能,比static_cast更安全狂鞋。在多態(tài)類型之間的轉(zhuǎn)換主要使用dynamic_cast片择,因為類型提供了運行時信息。

15骚揍、說幾個C++11的新特性字管。

1)auto類型推導(dǎo):auto vData = vectors.begin();自動推導(dǎo)出vector中的數(shù)據(jù)類型
2)范圍for循環(huán):for( auto v:vectors) { cout<<v<<endl; }這樣的v是只讀的,也可以用到&引用信不,作為可修改的for(auto &v:vectors){ v=v+1; }
3)lambda表達(dá)式:是一個匿名仿函數(shù)(函數(shù)對象)嘲叔,一定是[]中括號開頭。[可選的捕捉到的外部變量] (可選的形參傳遞)mutable可選 throwSpec可選 -> retType可選 { ... }抽活,當(dāng)中括號和大括號之間有東西時硫戈,也一定要有小括號。

4)override 和 final 關(guān)鍵字:兩個繼承控制關(guān)鍵字下硕,override明確地表示一個函數(shù)是對基類中一個虛函數(shù)的重寫丁逝,virtual void func(int) override;確保在派生類中聲明的重載函數(shù)跟基類的虛函數(shù)有相同的簽名汁胆。final阻止類的進一步派生class TaskManager {/*..*/} final;和虛函數(shù)的進一步重載virtual void func(int) override final;
5)空指針常量nullptr霜幼。C語言中NULL其實是define成0嫩码,所以兩者等價,但是因為C++支持函數(shù)重載罪既,那么在遇到函數(shù)f(int)f(void*)時铸题,f(0)和f(NULL)就會產(chǎn)生歧義不明確指向的是哪個,而有了f(nullptr)后就明確了是指向f(void*)琢感。
6)智能指針(模板類)shared_ptr(附帶weak_ptr)丢间、unique_ptr。shared_ptr采用引用計數(shù)的方式管理所指向的對象驹针。當(dāng)有一個新的shared_ptr指向同一個對象時(復(fù)制shared_ptr等)烘挫,引用計數(shù)加1。當(dāng)shared_ptr離開作用域時牌捷,引用計數(shù)減1墙牌。當(dāng)引用計數(shù)為0時,釋放所管理的內(nèi)存暗甥。weak_ptr一般和shared_ptr配合使用喜滨。它可以指向shared_ptr所指向的對象,但是卻不增加對象的引用計數(shù)撤防。這樣就有可能出現(xiàn)weak_ptr所指向的對象實際上已經(jīng)被釋放了的情況虽风。因此,weak_ptr有一個lock函數(shù)寄月,嘗試取回一個指向?qū)ο蟮膕hared_ptr辜膝。unique_ptr對于所指向的對象,正如其名字所示漾肮,是獨占的厂抖。所以,不可以對unique_ptr進行拷貝克懊、賦值等操作忱辅,但是可以通過release函數(shù)在unique_ptr之間轉(zhuǎn)移控制權(quán)。上述對于拷貝的限制谭溉,有兩個特殊情況墙懂,即unique_ptr可以作為函數(shù)的返回值和參數(shù)使用,這時雖然也有隱含的拷貝存在扮念,但是并非不可行的损搬。
1、所有的智能指針類都有一個explicit構(gòu)造函數(shù),以普通指針作為參數(shù)巧勤。因此不能自動將普通指針轉(zhuǎn)換為智能指針對象嵌灰,必須顯式調(diào)用構(gòu)造函數(shù)。 2踢关、所有智能指針都是對原始指針執(zhí)行淺拷貝伞鲫,即所有指針指向同一塊內(nèi)存粘茄,而不是深拷貝創(chuàng)建多個備份签舞。 3、當(dāng)一個shared_ptr對象sp2是由sp1拷貝構(gòu)造或者賦值構(gòu)造得來的時候柒瓣,實際上構(gòu)造完成后sp1內(nèi)部的__shared_count對象包含的指向管理對象的指針與sp2內(nèi)部的__shared_count對象包含的指向管理對象的指針是相等的儒搭,也就是說當(dāng)多個shared_ptr對象來管理同一個對象時,它們共同使用同一個動態(tài)分配的管理對象指針芙贫。 4搂鲫、都是運用了RAII技術(shù)來實現(xiàn)的。原生指針被轉(zhuǎn)為智能指針類使用之后磺平,就不要再使用原生指針魂仍,否則容易出錯。即不要把一個原生指針給多個shared_ptr管理拣挪。 5擦酌、引用頭文件#include<memory>。智能指針是模板類而不是指針菠劝。 6赊舶、智能指針實質(zhì)就是重載了->和*操作符的類,由類來實現(xiàn)對內(nèi)存的管理赶诊,確保即使有異常產(chǎn)生笼平,也可以通過智能指針類的析構(gòu)函數(shù)完成內(nèi)存的釋放。
7)initializer_list<>初始化列表舔痪,用于給變量定義時用{}賦初值寓调,如vector<int> v1{2,3,5};或者是直接用于函數(shù)的參數(shù)列表如max({5,2,8,1})得到8。這種對應(yīng)于{}大括號的初始化列表锄码,底層是一個array容器夺英,利用這個array先把初值開辟內(nèi)存,然后再調(diào)用對應(yīng)的接受initializer_list<>形參的構(gòu)造函數(shù)巍耗,然后進行數(shù)據(jù)的淺拷貝秋麸。
8)explicit關(guān)鍵字,多用于構(gòu)造函數(shù)前炬太,明確表示該構(gòu)造函數(shù)必須被顯示的調(diào)用灸蟆,而不能被用于隱式轉(zhuǎn)換調(diào)用(如多個參數(shù)中后面的參數(shù)有默認(rèn)值時,只給出前面參數(shù)一般也會觸發(fā)隱式轉(zhuǎn)換構(gòu)造),現(xiàn)在加了explicit關(guān)鍵字就是為了禁止這種隱式的轉(zhuǎn)換炒考。
9)decltype關(guān)鍵字可缚,用于從操作的對象或表達(dá)式中得到類型,如decltype(lambda)表示得到一個lambda對象的類型斋枢。
用途一:用于指明模板函數(shù)的返回類型帘靡。


用途二:用于傳遞lambda表達(dá)式對象的類型。

10)move移動語義瓤帚,對應(yīng)于右值引用描姚,可以將右值(只能放在賦值等號=右邊的變量,std::move(非右值)可以強制轉(zhuǎn)變?yōu)橛抑担┑馁Y源(內(nèi)存空間和值)竊取再直接賦給左值戈次,使得左值不用再定義和開辟額外的內(nèi)存轩勘,加快move拷貝的速度。常用于在構(gòu)造函數(shù)的時候新增一個類似于拷貝構(gòu)造(string(const string &str){ ... })的move構(gòu)造(string(string &&str){ ... })怯邪。本質(zhì)上是淺拷貝的行為绊寻,即拷貝的時候只是將原指針的值(變量內(nèi)存地址)賦給新的指針,然后原指針賦NULL不再使用悬秉,實際的那塊內(nèi)存沒有動澄步。相對的深拷貝就是對內(nèi)存中每一個數(shù)據(jù)都重新拷貝到了一塊新的內(nèi)存。
move移動語義右值引用.jpg

https://blog.csdn.net/FX677588/article/details/70157088
https://www.cnblogs.com/guxuanqing/p/6707824.html

16和泌、如何在一個不安全的環(huán)境中實現(xiàn)安全的數(shù)據(jù)通信村缸?

  • 要實現(xiàn)數(shù)據(jù)的安全傳輸,當(dāng)然就要對數(shù)據(jù)進行加密了允跑。
  • 如果使用對稱加密算法王凑,加解密使用同一個密鑰,除了自己保存外聋丝,對方也要知道這個密鑰索烹,才能對數(shù)據(jù)進行解密。如果你把密鑰也一起傳過去弱睦,就存在密碼泄漏的可能百姓。所以我們使用非對稱加密算法,過程如下:

首先 數(shù)據(jù)接收方 生成一對密鑰况木,即私鑰和公鑰垒拢;
然后,數(shù)據(jù)接收方 將公鑰發(fā)送給 數(shù)據(jù)發(fā)送方火惊;
數(shù)據(jù)發(fā)送方用收到的公鑰對數(shù)據(jù)加密求类,再發(fā)送給接收方;
接收方收到數(shù)據(jù)后屹耐,使用自己的私鑰解密尸疆。

》由于在非對稱算法中,公鑰加密的數(shù)據(jù)必須用對應(yīng)的私鑰才能解密,而私鑰又只有接收方自己知道寿弱,這樣就保證了數(shù)據(jù)傳輸?shù)陌踩浴?/p>

17犯眠、面向?qū)ο笏枷胫饕绾误w現(xiàn)?

  • 面向?qū)ο蟮娜筇匦允欠庋b症革、繼承筐咧、多態(tài)。
  • 實際中噪矛,對一個類的數(shù)據(jù)成員和方法成員進行封裝量蕊,對抽象基類的繼承可以很好的復(fù)用基類封裝的數(shù)據(jù)和方法成員,多態(tài)的應(yīng)用可以使父類指針指向子類的實例對象從而在程序運行時動態(tài)地選擇最合適的方法執(zhí)行摩疑。
  • 類與類之間的各種關(guān)系危融,也是面向?qū)ο蟮闹攸c,合理地利用類與類之間的關(guān)系可以設(shè)計出很多很好的設(shè)計模式雷袋。

18、類與類之間的各種關(guān)系辞居?

【繼承inheritance】A is-a B
類A繼承類B楷怒,UML類圖是實線加三角形由A指向B。A is-a B.
public繼承:父類中的成員訪問權(quán)限不變瓦灶。
pretected繼承:父類的訪問權(quán)限中public權(quán)限降為protected鸠删,其他不變。
private繼承:父類中public和protected權(quán)限的成員全變?yōu)閜rivate贼陶,權(quán)限降級刃泡,即子類只有類成員變量和友元函數(shù)能夠訪問這些。
【依賴dependency】A use-a B.
類A依賴類B碉怔,UML類圖是虛線加箭頭由A指向B烘贴。A use-a B.
一般來說,依賴是指A的某些方法功能要用到B撮胧,常表現(xiàn)為B作為A的成員方法的形參或局部變量或返回值桨踪,即只和類成員方法有關(guān)。
【關(guān)聯(lián)association】A has-a B, B has-a A.
類A和類B雙向關(guān)聯(lián)芹啥,UML類圖是A——B一根實線連接锻离。A與B互相作為類的數(shù)據(jù)成員變量。
【組合composition】A has-a B 實例對象
類A組合了類B墓怀,UML類圖是A實心菱形再實線和箭頭指向B汽纠。類A中定義了類B作為數(shù)據(jù)成員,B在A中定義構(gòu)造傀履。A和B的對象生命周期一樣虱朵。A擁有完整的B,強擁有關(guān)系。
【聚合aggregation】A has-a B 引用
類A聚合了類B卧秘,UML類圖是A空心菱形再實線和箭頭指向B呢袱。類A中定義了類B的指針作為數(shù)據(jù)成員,類B的實例化在其他地方翅敌。A和B的對象生命周期不一樣羞福。A擁有不完整的B,弱擁有蚯涮≈巫ǎ可以認(rèn)為是 composition by reference == aggregation 或者也可以叫做委托delegation。

19遭顶、談?wù)勀懔私獾脑O(shè)計模式有哪些张峰?

  • 設(shè)計模式本質(zhì)上是通過類與類之間的各種關(guān)系來實現(xiàn)的。
  • 【模板方法模式】技巧總結(jié):抽象父類+抽象方法+繼承+多態(tài)
  • 【單例模式】技巧總結(jié):聚合了一個自身的靜態(tài)對象指針+私有構(gòu)造+公有靜態(tài)getInstance()方法(懶漢模式)
  • 【簡單工廠模式】技巧總結(jié):繼承+依賴+多態(tài)+前后端分離
  • 【工廠方法模式】技巧總結(jié):繼承+依賴+多態(tài)+簡單工廠升級(增加具體的工廠子類)
  • 【策略模式】技巧總結(jié):繼承+依賴+多態(tài)+聚合(簡單工廠多一個聚合和多一個算法調(diào)用函數(shù))
  • 【觀察者模式】技巧總結(jié):繼承+聚合+依賴+多態(tài)

20棒旗、C++的拷貝構(gòu)造函數(shù)為什么要傳引用喘批?

  • 如string類的拷貝構(gòu)造string( const string & str ){ ... }參數(shù)傳遞了引用,使用時string str2(str1);铣揉,內(nèi)部其實是先把實參通過引用傳遞給形參饶深,const string & str=str1;沒有給str定義初始化,只是使用引用而已逛拱。
  • 如果進行值傳遞string( const string str ){ ... }敌厘,那么內(nèi)部其實是先有const string str=str1;等于是先給形參傳遞實參進行定義構(gòu)造,就又得進行一次拷貝構(gòu)造朽合,同樣之后會再次進行拷貝構(gòu)造俱两,導(dǎo)致無限遞歸進行拷貝構(gòu)造,直至爆棧程序結(jié)束曹步。

21宪彩、如何防止一個類被拷貝?——拷貝分為拷貝構(gòu)造和拷貝賦值箭窜。

  • 1是將拷貝構(gòu)造函數(shù)和拷貝賦值函數(shù)設(shè)為private私有的毯焕,這樣的話在使用到拷貝構(gòu)造和拷貝賦值時編譯就會報錯“設(shè)為私有的成員函數(shù)無法被對象訪問”。但是友元以及其他成員函數(shù)也還是可以使用(一般將成員函數(shù)中出現(xiàn)的本類對象默認(rèn)當(dāng)成是友元)磺樱。因此是非絕對的禁止拷貝纳猫。
  • 2是使用C++11的delete關(guān)鍵字玻驻,在拷貝構(gòu)造和拷貝賦值函數(shù)定義后面加上=delete;屯伞,如Test(const Test&) = delete;Test& operator=(const Test&) =delete;,那么就會將該類默認(rèn)提供的這兩個函數(shù)給delete刪除了簸喂,用的時候編譯會報錯“無法引用已刪除的函數(shù)”块差。

22侵续、帶指針成員變量的類編寫要注意什么倔丈?

  • 必須自定義拷貝構(gòu)造和拷貝賦值成員函數(shù),因為類默認(rèn)提供的這兩個函數(shù)只會完成對所有成員變量的數(shù)據(jù)淺拷貝状蜗,即指針也只會拷貝指針自身的值需五,而不會去深拷貝指針指向的內(nèi)容;因此在自定義的拷貝構(gòu)造和拷貝賦值成員函數(shù)中就要注意這個問題然后對指針指向的值就行深拷貝轧坎。還要注意宏邮,只要自定義了構(gòu)造、拷貝構(gòu)造缸血、賦值構(gòu)造蜜氨、析構(gòu)函數(shù),那么默認(rèn)的就自動失效捎泻,如果還想保留默認(rèn)的飒炎,需要=default。

23笆豁、什么是RAII 技術(shù)郎汪?

  • RAII(Resource Acquisition Is Initialization資源獲取時初始化)是一種利用對象生命周期來控制程序資源(如內(nèi)存、文件句柄渔呵、網(wǎng)絡(luò)連接怒竿、鎖互斥量、智能指針等等)的簡單技術(shù)扩氢。
  • RAII 的一般做法是這樣的:在對象構(gòu)造時獲取資源,接著控制對資源的訪問使之在對象的生命周期內(nèi)始終保持有效爷辱,最后在對象析構(gòu)的時候釋放資源录豺。借此,我們實際上把管理一份資源的責(zé)任托管給了一個對象饭弓。這種做法有兩大好處:
    1双饥、不需要顯式地釋放資源。
    2弟断、采用這種方式咏花,對象所需的資源在其生命期內(nèi)始終保持有效。
  • RAII在C++中的應(yīng)用非常廣泛阀趴,如C++標(biāo)準(zhǔn)庫中的lock_guard便是用RAII方式來控制互斥量昏翰,程序員可以非常方便地使用lock_guard,而不用擔(dān)心異常安全問題刘急。
template <class Mutex> class lock_guard {
private:
   Mutex& mutex_;

public:
   lock_guard(Mutex& mutex) : mutex_(mutex) { mutex_.lock(); }
   ~lock_guard() { mutex_.unlock(); }

   lock_guard(lock_guard const&) = delete;
   lock_guard& operator=(lock_guard const&) = delete;
};

void write_to_file(const std::string & message)
{
   // 創(chuàng)建關(guān)于文件的互斥鎖
   static std::mutex mutex;

   // 在訪問文件前進行加鎖棚菊,使用了局部對象lock
   std::lock_guard<std::mutex> lock(mutex);

   // 嘗試打開文件
   std::ofstream file("example.txt");
   if (!file.is_open())
       throw std::runtime_error("unable to open file");

   // 輸出文件內(nèi)容
   file << message << std::endl;

   // 當(dāng)離開作用域時,文件句柄會被首先析構(gòu) (不管是否拋出了異常)
   // 互斥鎖lock對象也會被析構(gòu) (同樣地叔汁,不管是否拋出了異常)
}
  • 當(dāng)一個函數(shù)需要通過多個局部變量來管理資源時统求,RAII就顯得非常好用检碗。因為只有被構(gòu)造成功(構(gòu)造函數(shù)沒有拋出異常)的對象才會在返回時調(diào)用析構(gòu)函數(shù)[4],同時析構(gòu)函數(shù)的調(diào)用順序恰好是它們構(gòu)造順序的反序[5]码邻,這樣既可以保證多個資源(對象)的正確釋放折剃,又能滿足多個資源之間的依賴關(guān)系。
    由于RAII可以極大地簡化資源管理像屋,并有效地保證程序的正確和代碼的簡潔怕犁,所以通常會強烈建議在C++中使用它。

24.1开睡、epoll如此高效的原因因苹?

  • 這是由于我們在調(diào)用epoll_create時,Linux內(nèi)核會創(chuàng)建一個eventpoll結(jié)構(gòu)體對象篇恒,內(nèi)核除了幫我們在epoll文件系統(tǒng)里建了個file結(jié)點扶檐,在內(nèi)核cache里建了個【紅黑樹】用于存儲以后epoll_ctl傳來的要監(jiān)聽的socket事件外;還會再建立一個list鏈表胁艰,用于存儲準(zhǔn)備就緒的socket事件款筑;當(dāng)epoll_wait調(diào)用時,僅僅觀察這個list鏈表里有沒有數(shù)據(jù)(有事件發(fā)生的socket句柄)即可腾么。有數(shù)據(jù)就返回奈梳,沒有數(shù)據(jù)就sleep,等到timeout時間到后即使鏈表沒數(shù)據(jù)也返回解虱。所以攘须,epoll_wait非常高效。

  • 這個準(zhǔn)備就緒list鏈表是怎么維護的呢殴泰?當(dāng)我們執(zhí)行epoll_ctl時于宙,除了把socket句柄放到epoll文件系統(tǒng)里file對象對應(yīng)的紅黑樹上之外,還會給內(nèi)核中斷處理程序注冊一個回調(diào)函數(shù)悍汛,告訴內(nèi)核捞魁,如果這個句柄的中斷到了,就把它放到準(zhǔn)備就緒list鏈表里离咐。所以谱俭,當(dāng)一個socket上有數(shù)據(jù)到了,內(nèi)核在把網(wǎng)卡上的數(shù)據(jù)copy到內(nèi)核中后就來把socket插入到準(zhǔn)備就緒鏈表里了宵蛀。(注:好好理解這句話@ブ)

  • 從上面這句可以看出,epoll的基礎(chǔ)就是回調(diào)呀糖埋!

  • 如此宣吱,一顆紅黑樹,一張準(zhǔn)備就緒句柄鏈表瞳别,少量的內(nèi)核cache征候,就幫我們解決了大并發(fā)下的socket處理問題杭攻。執(zhí)行epoll_create時,創(chuàng)建了紅黑樹和就緒鏈表疤坝,執(zhí)行epoll_ctl時兆解,如果增加socket句柄,則檢查在紅黑樹中是否存在跑揉,存在立即返回锅睛,不存在則添加到樹干上,然后向內(nèi)核注冊回調(diào)函數(shù)历谍。當(dāng)網(wǎng)卡監(jiān)聽到socket數(shù)據(jù)變化時现拒,就查找該紅黑樹是否有監(jiān)聽對應(yīng)的socket(紅黑樹查找速度很快),找到紅黑樹中有該socket時引發(fā)中斷事件望侈,向準(zhǔn)備就緒鏈表中插入該socket句柄數(shù)據(jù)印蔬。執(zhí)行epoll_wait時立刻返回準(zhǔn)備就緒鏈表里的數(shù)據(jù)即可。
  • https://blog.csdn.net/tianjing0805/article/details/76021440

24.2脱衙、epoll的水平觸發(fā)和邊沿觸發(fā)侥猬?

  • 水平觸發(fā)(LT):當(dāng)epoll檢測到其上有事件發(fā)生并通知應(yīng)用程序時,應(yīng)用程序可以不立即處理捐韩,這樣當(dāng)應(yīng)用程序再次調(diào)用epoll中調(diào)用函數(shù)時退唠,epoll會再次通知應(yīng)用程序此事件,直到被處理。
    邊沿觸發(fā)(ET):當(dāng)epoll檢測到其上有事件發(fā)生并通知應(yīng)用程序時荤胁,應(yīng)用程序必須立即處理瞧预,并且下一次的epoll調(diào)用,不會再向應(yīng)用程序通知此事件仅政。
    所以ET模式大大得降低了同一個epoll事件被重復(fù)觸發(fā)的次數(shù)松蒜,因此ET模式工作效率比LT模式更高。
    select已旧、poll、epoll的默認(rèn)工作模式都是水平觸發(fā)(LT)模式召娜,但是epoll是支持邊沿觸發(fā)(ET)模式的运褪。如果將ET模式代碼中的event.events = EPOLLIN | EPOLLET;改成event.events = EPOLLIN;,即使用LT模式玖瘸〗斩铮可見ET,LT模式是針對具體的某個事件來設(shè)置的
    select/poll會因為監(jiān)聽fd的數(shù)量而導(dǎo)致效率低下,因為它是輪詢所有fd雅倒,有數(shù)據(jù)就處理璃诀,沒數(shù)據(jù)就跳過,所以fd的數(shù)量會降低效率蔑匣;而epoll只處理就緒的fd劣欢,它有一個就緒設(shè)備的鏈表棕诵,每次只輪詢該鏈表的數(shù)據(jù),然后進行處理凿将。
  • LT, ET這件事怎么做到的呢校套?當(dāng)一個socket句柄上有事件時,內(nèi)核會把該句柄插入上面所說的準(zhǔn)備就緒list鏈表牧抵,這時我們調(diào)用epoll_wait笛匙,會把準(zhǔn)備就緒的socket拷貝到用戶態(tài)內(nèi)存,然后清空準(zhǔn)備就緒list鏈表犀变,最后妹孙,epoll_wait干了件事,就是檢查這些socket获枝,如果不是ET模式(就是LT模式的句柄了)蠢正,并且這些socket上確實有未處理的事件時,又把該句柄放回到剛剛清空的準(zhǔn)備就緒鏈表了映琳。所以机隙,非ET的句柄,只要它上面還有事件萨西,epoll_wait每次都會返回這個句柄有鹿。(從上面這段,可以看出谎脯,LT還有個回放的過程葱跋,低效了)

25、Linux下的三個特殊進程

  • Linux下有三個特殊的進程idle進程(PID=0)源梭,init進程(PID=1)娱俺,和kthreadd(PID=2)
  • idle(空閑)進程由系統(tǒng)自動創(chuàng)建,運行在內(nèi)核態(tài)
    idle進程其pid=0废麻,其前身是系統(tǒng)創(chuàng)建的第一個進程荠卷,也是唯一一個沒有通過fork或者kernel_thread產(chǎn)生的進程。完成加載系統(tǒng)后烛愧,演變?yōu)檫M程調(diào)度油宜、交換。
  • kthreadd進程由idle通過kernel_thread創(chuàng)建怜姿,并始終運行在內(nèi)核空間慎冤,負(fù)責(zé)所有內(nèi)核進程的調(diào)度和管理。
    它的任務(wù)就是管理和調(diào)度其他內(nèi)核線程kernel_thread, 會循環(huán)執(zhí)行一個kthread的函數(shù)沧卢,該函數(shù)的作用就是運行kthread_create_list全局鏈表中維護的kthread, 當(dāng)我們調(diào)用kernel_thread創(chuàng)建的內(nèi)核線程會被加入到此鏈表中蚁堤,因此所有的內(nèi)核線程都是直接或者間接的以kthreadd為父進程 。
  • init進程由idle通過kernel_thread創(chuàng)建但狭,在內(nèi)核空間完成初始化后披诗,加載init程序
    在這里我們就主要講解下init進程撬即,init進程由0進程創(chuàng)建,完成系統(tǒng)的初始化藤巢,是系統(tǒng)中所有其他用戶進程的祖先進程
    Linux中的所有進程都是由init進程創(chuàng)建并運行的搞莺。首先Linux內(nèi)核啟動,然后在用戶空間中啟動init進程掂咒,再啟動其他系統(tǒng)進程才沧。在系統(tǒng)啟動完成后,init將變成為守護進程監(jiān)視系統(tǒng)其他進程绍刮。
    所以說init進程是Linux系統(tǒng)操作中不可缺少的程序之一温圆,如果內(nèi)核找不到init進程就會試著運行/bin/sh,如果運行失敗孩革,系統(tǒng)的啟動也會失敗岁歉。

26、平衡二叉樹和紅黑樹的區(qū)別膝蜈?

  • 平衡二叉樹AVL是指嚴(yán)格的平衡二叉樹——即每個樹結(jié)點的左右子樹的深度差值(平衡因子)不超過1锅移。
  • 紅黑樹只是弱平衡二叉樹,不是完全遵守平衡因子不超過1的饱搏,也就是說可能會出現(xiàn)某結(jié)點左右子樹的深度差值大于1非剃,因此紅黑樹是相對平衡的二叉樹。
  • 那為什么紅黑樹比AVL平衡二叉樹使用的地方更多呢推沸?
  • 因為AVL為了保持嚴(yán)格平衡备绽,每次有數(shù)據(jù)插入或刪除后都需要做更多的檢查調(diào)整工作,效率較低鬓催。
    而紅黑樹不需要維持嚴(yán)格平衡肺素,因此插入和刪除效率更高。
  • 就查找效率來說宇驾,AVL因為嚴(yán)格平衡樹的深度大概率會比紅黑樹更小倍靡,因此查找效率會更高。
  • 總結(jié)课舍,AVL更適合于插入和刪除不頻繁而查找頻繁的地方菌瘫;而紅黑樹兼顧了插入、刪除和查詢的性能布卡,所以適用范圍更廣。

三雇盖、STL

1忿等、STL的作用,為什么需要STL崔挖?(常用的是SGI版本的STL贸街,被納入GNU C++標(biāo)準(zhǔn)庫)

  • 一是可復(fù)用性庵寞。STL標(biāo)準(zhǔn)模板庫,提供了一套常用的標(biāo)準(zhǔn)的數(shù)據(jù)結(jié)構(gòu)和算法程序以及和泛型模板編程結(jié)合薛匪,我們在寫程序時捐川,可以快速復(fù)用,而不需要重復(fù)的去造輪子逸尖,要站在巨人的肩膀上古沥,節(jié)約時間。
  • 二是高效性娇跟。STL的代碼經(jīng)過多位C++大師的優(yōu)化岩齿,比絕大多數(shù)人自己再重寫相關(guān)的數(shù)據(jù)結(jié)構(gòu)和算法代碼效率更高,因此我們應(yīng)該盡量多用STL苞俘。
  • 研究STL源碼的意義在于盹沈,深入理解和學(xué)習(xí)優(yōu)秀的數(shù)據(jù)結(jié)構(gòu)和算法原理,然后擴展輪子吃谣,或者是深入定制自己的輪子乞封。
  • STL更注重的是模板泛型編程,而不是面向?qū)ο缶幊獭?/li>

2岗憋、STL六大組件之間的關(guān)系肃晚?

  • 六大組件:空間配置器(allocator)、容器(container)澜驮、迭代器(iterator)陷揪、算法(algorithm)、仿函數(shù)(functor)[函數(shù)對象]杂穷、適配器(adapter)[包裝器]悍缠。
  • 空間配置器為容器分配內(nèi)存,容器的模板參數(shù)中都有一個默認(rèn)的空間配置器class Alloc = alloc耐量。最簡單的空間配置器實現(xiàn)就是類內(nèi)部用alllocate成員函數(shù)封裝::operator new(相當(dāng)于malloc)分配內(nèi)存飞蚓,再用construct成員函數(shù)負(fù)責(zé)對象的構(gòu)造(實際上是借用定位new——placement new來給這塊內(nèi)存初始化的buffer = new(buffer) T(value);其中T(value)就用到了構(gòu)造函數(shù));用destroy成員函數(shù)完成析構(gòu)廊蜒,再用deallocate成員函數(shù)封裝::operator delete(相當(dāng)于free)趴拧,來實現(xiàn)對象的內(nèi)存分配和構(gòu)造初始化以及用完后析構(gòu)和釋放內(nèi)存。而這些分步也是實現(xiàn)常用的new/delete表達(dá)式的步驟山叮。STL提供的空間配置器分為第一級配置器和第二級配置器著榴。
  • 容器分為序列式容器(vector/deque/list/forward_list)和關(guān)聯(lián)式容器(set/map/unordered_set/unordered_map)。
  • 迭代器作為容器和算法的粘合劑屁倔,算法函數(shù)的傳入?yún)?shù)一般都是容器的迭代器脑又。指針也是一種特特殊的迭代器。迭代器有5中類型:
    1)input iterator:只讀入數(shù)據(jù)的迭代器,不能通過它修改指向的元素值
    2)output iterator:只寫入數(shù)據(jù)的迭代器
    3)forward iterator:包含上面的可讀可寫功能问麸,然后只有operator++向前單步移動的功能往衷。
    4)bidirectional iterator:包含上面的讀寫功能,可雙向單步移動严卖,支持operator++和--席舍。
    5)random access iterator:隨機存取迭代器,不僅可讀可寫哮笆,還支持順序存儲如用數(shù)組下標(biāo)的跳步移動訪問功能来颤。
  • 算法
  • 仿函數(shù)又叫函數(shù)對象,常用類名加()代表一個臨時對象疟呐,可為算法動態(tài)地提供某種策略脚曾。類中一定重載了operator()。比如常用的將算法庫中的sort函數(shù)的比較策略用一個仿函數(shù)傳入启具,當(dāng)然也可傳入一個函數(shù)指針或是對類型重載<號本讥。一般函數(shù)指針可以當(dāng)做仿函數(shù)對象用。
  • 適配器類的內(nèi)部封裝(組合)了一個其他類的對象鲁冯。如容器適配器stack/queue默認(rèn)就是在內(nèi)部組合了一個deque<T> dq;拷沸,該deque對象的生命周期和stack/queue對象一樣,然后通過封裝dq對象的某些成員函數(shù)來實現(xiàn)自己的功能薯演。priority_queue也是以vector為底層容器的完全二叉樹的容器適配器撞芍。前面說的三種容器適配器都不提供迭代器不能遍歷。還有仿函數(shù)適配器跨扮;迭代器適配器序无。

3、vector衡创、map/multimap帝嗡、unordered_map/unordered_multimap的底層數(shù)據(jù)結(jié)構(gòu),以及幾種map容器如何選擇璃氢?

  • 底層數(shù)據(jù)結(jié)構(gòu):vector基于數(shù)組哟玷,map/multimap基于紅黑樹,unordered_map/unordered_multimap基于哈希表一也。
    根據(jù)應(yīng)用場景進行選擇:
    map/unordered_map 不允許重復(fù)元素
    multimap/unordered_multimap允許重復(fù)元素
    map/multimap底層基于紅黑樹巢寡,元素自動有序,且插入椰苟、刪除效率高
    unordered_map/unordered_multimap底層基于哈希表抑月,故元素?zé)o序,查找效率高舆蝴。

4爪幻、type_traits<T>是什么作用菱皆?

  • 這個的作用是對類型T進行一些和類型相關(guān)的詢問,然后返回true或false的相關(guān)回答(即對類型T萃取出相關(guān)類型說明)挨稿。也有iterator_traits可以萃取出一個迭代器的相關(guān)類型。
    比如在空間配置器alloctor的實現(xiàn)中京痢,有個destroy成員函數(shù)在對數(shù)組中的所有成員析構(gòu)時奶甘,會先用type_traits<T>::has_trivial_destructor檢查成員類型的析構(gòu)函數(shù)是否是trivial(不重要的),若是返回__false_type則說明這個類型T的析構(gòu)函數(shù)是重要的祭椰,必須對數(shù)組中每個對象依次調(diào)用析構(gòu)函數(shù)臭家。相反,若是返回__true_type說明確實是不重要的析構(gòu)函數(shù)(甚至是只有默認(rèn)隱藏的空析構(gòu)函數(shù))方淤,則不需要去遍歷數(shù)組對象調(diào)用析構(gòu)函數(shù)钉赁,可以大大提高效率。

5携茂、STL的空間配置器實現(xiàn)原理你踩?

  • STL空間配置器的作用是維護對象的內(nèi)存分配、釋放以及構(gòu)造讳苦、析構(gòu)带膜。因此我們可以從這四個作用來看實現(xiàn)原理,空間配置器中一定實現(xiàn)了allocate/deallocate/contructor/destroy這四個成員函數(shù)鸳谜。這幾個成員函數(shù)如何實現(xiàn)又要根據(jù)使用的空間配置器是第一級還是第二級膝藕。
  • STL的空間配置器分為第一級__malloc_alloc_template和第二級__default_alloc_template,第一級用于處理從堆中大塊內(nèi)存的申請(一次128字節(jié)以上算一大塊咐扭?)芭挽,第二級用于處理從自定義的內(nèi)存池中取得小塊內(nèi)存的申請。默認(rèn)使用第二級蝗肪,但是在第二級的allocate會判斷要申請的字節(jié)數(shù)n若是大于128則直接跳轉(zhuǎn)到第一級袜爪,否則再從第二級內(nèi)存池中對應(yīng)的鏈表中獲取合適的區(qū)塊。
  • 第一級配置器就是使用malloc/free/realloc這幾個C語言函數(shù)從堆中分配釋放內(nèi)存給用戶穗慕,并且自己寫了處理內(nèi)存不足的函數(shù)set_malloc_handle()饿敲,大致思想就是用一個無限循環(huán)來不斷嘗試分配內(nèi)存(可能程序在剛開始時就預(yù)先分配了大塊內(nèi)存?zhèn)溆茫@時候就可以釋放一部分出來解決內(nèi)存分配不足問題)逛绵,直到分配成功怀各。如果不寫這個處理函數(shù),那就只能返回內(nèi)存分配不足的異呈趵耍或直接退出程序瓢对。
  • 第二級配置器(又叫次層配置sub-allocation)是維護了一個內(nèi)存池,處理頻繁的小塊內(nèi)存的申請胰苏,相比第一級從堆中申請不僅速度更快還更有線程安全性硕蛹,也能更好地處理內(nèi)存碎片的問題節(jié)約內(nèi)存。內(nèi)存池用一個數(shù)組free_list[16]維護了128/8=16個鏈表,分別存放區(qū)塊大小為{8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128}法焰。首次從對應(yīng)鏈表中比如16申請區(qū)塊時秧荆,會調(diào)用refill(n=16)函數(shù)先分配20個區(qū)塊給區(qū)塊大小n=16的鏈表備用。
  • void * allocate(size_t n)申請內(nèi)存過程:若是n>128那就轉(zhuǎn)到第一級配置器去調(diào)用malloc從堆中申請埃仪,否則默認(rèn)按照第二級配置器從內(nèi)存池申請乙濒。比如你要申請n=10的一塊內(nèi)存,就可以向上升級到8的倍數(shù)16那個free_list[16/8-1]的那個鏈表中獲取一個元素(區(qū)塊)卵蛉。鏈表中的元素(區(qū)塊)obj也有意思颁股,是一個union聯(lián)合體,在分配給用戶使用前將obj->free_list_link賦值給對應(yīng)鏈表的首元素my_free_list(由于該變量是obj * volatile * 類型傻丝,直接指向了實際內(nèi)存而不是存放在臨時寄存器中甘有,因此后面給它重新復(fù)制會被立即修改,而不用擔(dān)心多線程問題)葡缰,以便下次再從這個鏈表申請內(nèi)存時可以立即獲取一個空閑內(nèi)存塊亏掀。然后再分配給用戶使用,從第一個char字節(jié)開始的內(nèi)存运准。
    若是從鏈表中獲取不到內(nèi)存則要從內(nèi)存池中重新分配區(qū)塊到鏈表幌氮,若是內(nèi)存池中沒有了足夠的內(nèi)存那就要從堆中分配兩倍多的內(nèi)存到內(nèi)存池,若是堆中內(nèi)存不夠則可以拆分大區(qū)塊鏈表的內(nèi)存胁澳,若是都獲取不到內(nèi)存该互,那就轉(zhuǎn)到第一級配置的set_malloc_handle()去處理內(nèi)存分配失敗。
union obj{
    union obj * free_list_link;    //保存著下一個空閑區(qū)塊的地址
    char client_data;    //表示給用戶申請使用后的數(shù)據(jù)存放首字節(jié)地址
};
  • void deallocate(void *p, size_t n); 釋放內(nèi)存過程:若是n>128韭畸,則直接轉(zhuǎn)到第一級配置器去調(diào)用free釋放內(nèi)存到堆宇智,否則默認(rèn)按照第二級配置器的流程歸還到內(nèi)存池中16個鏈表對應(yīng)的那個。
  • 內(nèi)存池設(shè)計:兩根指針start_free和end_free分別代表池中自由的內(nèi)存的首字節(jié)和尾字節(jié)地址胰丁;將要申請的字節(jié)數(shù)擴展到8的倍數(shù)的函數(shù)随橘;一個指針數(shù)組free_list維護16個鏈表的區(qū)塊的首地址;一個union obj表示一個區(qū)塊锦庸,內(nèi)含下一個區(qū)塊的指針和首地址机蔗;allocate分配內(nèi)存函數(shù)返回分配的區(qū)塊的首地址若是大于128則轉(zhuǎn)到第一級配置從malloc獲取甘萧;refill()函數(shù)重新填充該種區(qū)塊的鏈表萝嘁,內(nèi)部調(diào)用chunk_alloc()函數(shù)從內(nèi)存池獲取一大塊內(nèi)存,再轉(zhuǎn)換成多個小區(qū)快(最多20塊)并循環(huán)鏈接起來扬卷;chunk_alloc()負(fù)責(zé)提供大塊的區(qū)塊牙言,管理內(nèi)存池中剩余的區(qū)塊,不夠時從堆空間獲取兩倍多的需求填充內(nèi)存池怪得;deallocate頭插法歸還內(nèi)存到對應(yīng)鏈表的首部咱枉。

6卑硫、STL中vector內(nèi)存動態(tài)擴展的倍數(shù)是多少?設(shè)為多少比較合適蚕断?為什么

  • 不同的STL庫實現(xiàn)欢伏,可能會設(shè)置為不同的倍數(shù),大都是設(shè)為2或者1.5倍亿乳。理論上來講設(shè)為1.5倍更好颜懊,更有利于后面擴展內(nèi)存時復(fù)用前面釋放的內(nèi)存(重新從原開始釋放處向后取得一塊足夠大小的連續(xù)內(nèi)存)。

https://www.nowcoder.com/discuss/90907?type=2&order=0&pos=3&page=1
http://www.cnblogs.com/webary/p/4754522.html
https://blog.csdn.net/lisonglisonglisong/article/details/51327586

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末风皿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子匠璧,更是在濱河造成了極大的恐慌桐款,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夷恍,死亡現(xiàn)場離奇詭異魔眨,居然都是意外死亡,警方通過查閱死者的電腦和手機酿雪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門遏暴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人指黎,你說我怎么就攤上這事朋凉。” “怎么了醋安?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵杂彭,是天一觀的道長。 經(jīng)常有香客問我吓揪,道長亲怠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任柠辞,我火速辦了婚禮团秽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叭首。我一直安慰自己习勤,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布放棒。 她就那樣靜靜地躺著姻报,像睡著了一般。 火紅的嫁衣襯著肌膚如雪间螟。 梳的紋絲不亂的頭發(fā)上吴旋,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天损肛,我揣著相機與錄音,去河邊找鬼荣瑟。 笑死治拿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的笆焰。 我是一名探鬼主播劫谅,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嚷掠!你這毒婦竟也來了捏检?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤不皆,失蹤者是張志新(化名)和其女友劉穎贯城,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體霹娄,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡湖蜕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年姿鸿,在試婚紗的時候發(fā)現(xiàn)自己被綠了咳短。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勋乾。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖枕磁,靈堂內(nèi)的尸體忽然破棺而出渡蜻,到底是詐尸還是另有隱情,我是刑警寧澤透典,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布晴楔,位于F島的核電站,受9級特大地震影響峭咒,放射性物質(zhì)發(fā)生泄漏税弃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一凑队、第九天 我趴在偏房一處隱蔽的房頂上張望则果。 院中可真熱鬧,春花似錦漩氨、人聲如沸西壮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽款青。三九已至,卻和暖如春霍狰,著一層夾襖步出監(jiān)牢的瞬間抡草,已是汗流浹背饰及。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留康震,地道東北人燎含。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像腿短,于是被迫代替她去往敵國和親屏箍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • C++運算符重載-下篇 本章內(nèi)容:1. 運算符重載的概述2. 重載算術(shù)運算符3. 重載按位運算符和二元邏輯運算符4...
    Haley_2013閱讀 1,435評論 0 49
  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學(xué)習(xí)記錄文檔橘忱,今天18年5月份再次想寫文章赴魁,發(fā)現(xiàn)簡書還為我保存起的...
    Jenaral閱讀 2,732評論 2 9
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,745評論 0 38
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,506評論 1 51
  • 時間過得真快,距離上次我在簡書更文已經(jīng)過去了兩個月的時間了钝诚。 我想談?wù)剝蓚€月里發(fā)生的事情尚粘,不為瀏覽,只想簡單的陳述...
    幾許c閱讀 415評論 2 3