關(guān)于C++及操作系統(tǒng)的一些知識(shí)點(diǎn)

1. 類的默認(rèn)成員函數(shù)

包括6個(gè):構(gòu)造函數(shù)、析構(gòu)函數(shù)、拷貝構(gòu)造函數(shù)、賦值運(yùn)算符函數(shù)、取址運(yùn)算符函數(shù)、const取址運(yùn)算符函數(shù),用一個(gè)自己實(shí)現(xiàn)的string類來展示這幾個(gè)默認(rèn)成員函數(shù)。C++11又增加了默認(rèn)的移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符函數(shù)。

class String

{

public:

? ? virtual ~String();

? ? String(const char * str);

? ? String(const String & other);

? ? String & operator = (const String & other);

? ? String * operator & ();

? ? const String * operator & () const;


private:

? ? char * m_data;

};

String::String(const char * str)

{

? ? if (nullptr == str)

? ? {

? ? ? ? m_data = new char[1];

? ? ? ? *m_data = '\0';

? ? }

? ? else

? ? {

? ? ? ? m_data = new char[strlen(str) + 1];

? ? ? ? strcpy(m_data, str);

? ? }

}

String::~String()

{

? ? delete [] m_data;

? ? m_data = nullptr;

}

String::String(const String & other)

{

? ? m_data = new char[strlen(other.m_data)+1];

? ? strcpy(m_data, other.m_data);

}

String & String::operator = (const String & other)

{

? ? if (this == &other) return *this;

? ? delete [] m_data;

? ? m_data = new char[strlen(other.m_data)+1];

? ? strcpy(m_data, other.m_data);

? ? return *this;

}

String * String::operator & ()

{

? ? return this;

}

const String * String::operator & () const

{

? ? return this;

}

2.C++11 的default和delete

C++11引入了default和delete對(duì)類的默認(rèn)成員函數(shù)進(jìn)行控制,這樣比將某些不想實(shí)現(xiàn)或者暴露的默認(rèn)成員函數(shù)置為私有成員更優(yōu)雅一些。

3.初始化表

,是構(gòu)造函數(shù)的一種初始化方式,使用初始化表對(duì)非內(nèi)部數(shù)據(jù)類型的類成員進(jìn)行初始化的效率更高,因?yàn)椴恍枰~外調(diào)用該類型成員的無參構(gòu)造函數(shù),只需要調(diào)用拷貝構(gòu)造函數(shù)即可完成初始化。另外,如果類存在繼承關(guān)系,派生類必須在其初始化表中調(diào)用基類的構(gòu)造函數(shù)。

4.奇異遞歸模板模式

(CRTP,Curiously Recurring Template Pattern)

這篇寫的不錯(cuò):

奇異遞歸模板模式

5.RAII

Resource Acquisition is Initialization,意為“資源獲取即初始化”,智能指針shared_ptr與auto_ptr是RAII最具代表性的實(shí)現(xiàn),另外一個(gè)很有用的地方是使用std::unique_lock或者std::lock_guard對(duì)互斥量std:: mutex進(jìn)行狀態(tài)管理。

6.C++11提供了auto關(guān)鍵字和區(qū)間迭代

結(jié)合stl容器使用,能簡化for循環(huán)寫法,非常優(yōu)雅。

std::map hash_map = {{1, “c++”}, {2, “java”}, {3, “python”}};?

for(auto it : hash_map) {

? ? std::cout << it.first << “\t” << it.second << std::endl;?

}?

7.虛函數(shù)

主要為了實(shí)現(xiàn)多態(tài),可以讓父類指針方便調(diào)用子類的實(shí)現(xiàn)。如果類中存在虛函數(shù),那么在類對(duì)象的內(nèi)存起始處會(huì)建立虛表v-table,所有虛函數(shù)的地址可以在虛表中找到,存在繼承的情況下,如果有函數(shù)覆蓋,那么在子類對(duì)象的虛表中,子類的虛函數(shù)地址會(huì)替換掉父類的虛函數(shù)地址。vtable在32位程序下占用4字節(jié),在64位程序下占用8字節(jié)。

虛函數(shù)表的存儲(chǔ)位置,可能根據(jù)編譯器的實(shí)現(xiàn)各有不同,根據(jù)網(wǎng)友深挖,其在gcc編譯器下位于只讀數(shù)據(jù)段.rodata中,而在vs編譯器下位于常量段中。

純虛函數(shù),就像一個(gè)接口定義一樣,定義的時(shí)候是不做實(shí)現(xiàn)的,必須在子類中實(shí)現(xiàn),擁有純虛函數(shù)的類不能生成對(duì)象,叫抽象類。

8.內(nèi)存布局

包括程序的內(nèi)存布局和類對(duì)象的內(nèi)存布局,熟悉經(jīng)典的鉆石繼承,可以方便了解類對(duì)象的內(nèi)存布局,引入虛擬繼承,可以解決鉆石繼承面臨的二義性問題,而且節(jié)省了變量存儲(chǔ)空間。


繼承的對(duì)象模型


內(nèi)存存儲(chǔ)區(qū)示意圖

9.sizeof,是在編譯的時(shí)候計(jì)算大小。

空類/空結(jié)構(gòu)體的sizeof值為1。試想一個(gè)“不占空間“的變量如何被取地址、兩個(gè)不同的“空結(jié)構(gòu)體”變量又如何得以區(qū)分呢,于是,“空結(jié)構(gòu)體”變量也得被存儲(chǔ),這樣編譯器也就只能為其分配一個(gè)字節(jié)的空間用于占位了。sizeof的值不包含靜態(tài)變量。

10.字節(jié)對(duì)齊,

也叫內(nèi)存對(duì)齊,關(guān)于字節(jié)對(duì)齊,這篇文章寫的不錯(cuò):

理一理字節(jié)對(duì)齊的那些事

字節(jié)對(duì)齊原因:

總線從內(nèi)存讀取數(shù)據(jù),并不是一個(gè)字節(jié)一個(gè)字節(jié)的讀取,而是一次讀取多個(gè)字節(jié),不同計(jì)算機(jī)實(shí)現(xiàn)可能不同,導(dǎo)致cpu字長不同,一般都是2字節(jié)的整數(shù)倍字節(jié)數(shù)來讀取,為了避免一個(gè)變量因?yàn)榈刂菲撇灰?guī)整而被多次讀取影響效率,需要靠字節(jié)對(duì)齊來解決這種問題。

字節(jié)對(duì)齊原則:

結(jié)構(gòu)體變量的首地址能夠被其對(duì)齊字節(jié)數(shù)大小所整除。

結(jié)構(gòu)體每個(gè)成員相對(duì)結(jié)構(gòu)體首地址的偏移都是成員大小的整數(shù)倍,如不滿足,對(duì)前一個(gè)成員填充字節(jié)以滿足。

結(jié)構(gòu)體的總大小為結(jié)構(gòu)體對(duì)齊字節(jié)數(shù)大小的整數(shù)倍,如不滿足,最后填充字節(jié)以滿足。

字節(jié)對(duì)齊跨平臺(tái)問題:

不同平臺(tái)的對(duì)齊方案可能不同,所以如果是跨平臺(tái)傳遞數(shù)據(jù),需要編譯器介入,改變各平臺(tái)的對(duì)齊方式為約定方式,比如#pragma pack(1),設(shè)置為1字節(jié)對(duì)齊。

11.return局部變量的原理,

如果是內(nèi)置數(shù)據(jù)類型的局部變量,會(huì)先將變量值寄存到寄存器中,然后將寄存器中的值拷貝給調(diào)用方,如果是類對(duì)象,那么編譯器會(huì)先在棧中生成一個(gè)匿名對(duì)象,然后調(diào)用復(fù)制構(gòu)造函數(shù)將局部變量值賦值給匿名對(duì)象,再將匿名對(duì)象的地址存入寄存器,然后再將寄存器地址拷貝給調(diào)用方使用。

很明顯,上面這種方式,產(chǎn)生了臨時(shí)變量,是很影響效率的,所以編譯器進(jìn)行了返回值優(yōu)化RVO和具名返回值優(yōu)化NRVO,優(yōu)化之后,不產(chǎn)生臨時(shí)變量,也能達(dá)到原有效果!

12.C++11是從gcc4.7版本以后才開始支持,

這幾年版本更新比較多,出現(xiàn)了C++14和C++17,可能在明年2020年會(huì)出臺(tái)C++20標(biāo)準(zhǔn)了,C++20要支持協(xié)程!


C++標(biāo)準(zhǔn)迭代表

13.32位系統(tǒng)與64位的區(qū)別

第一個(gè)是cpu運(yùn)算器和寄存器支持的數(shù)據(jù)范圍更大,第二個(gè)是尋址空間更大,突破4GB的限制,到達(dá)4EB,所以可以支持更大的內(nèi)存,以及更大的視頻文件,第三個(gè)是cpu字長更長,使得大數(shù)的計(jì)算速度更快。

處理器中的寄存器通??煞譃槿N︰整數(shù)、浮點(diǎn)數(shù)、其它。在所有常見的主流處理器中,只有整數(shù)寄存器(integer register)才可存放指針值(內(nèi)存數(shù)據(jù)的地址)。非整數(shù)寄存器不能存放指針來讀寫內(nèi)存,因此不能用來避開任何受到整數(shù)寄存器大小所影響的內(nèi)存限制。

32位程序遷移到64位系統(tǒng)上編譯,如果程序?qū)懛ㄉ喜灰?guī)范,可能帶來錯(cuò)誤,比如把long賦值給int,或者沒有使用sizeof來獲取長度而是在代碼里寫死。

64位系統(tǒng)相對(duì)于32位系統(tǒng)的缺點(diǎn),主要是相同的數(shù)據(jù)會(huì)消耗更多的內(nèi)存空間,比如long類型數(shù)據(jù),如果沒有超過4.2億以上的數(shù)字需要存儲(chǔ),那么內(nèi)存空間會(huì)浪費(fèi)一倍。所以64位進(jìn)程對(duì)內(nèi)存的需求量更大。

14.嚴(yán)格弱序,

這是stl對(duì)有序的關(guān)聯(lián)容器的要求,如set/map/multiset/multimap等容器,嚴(yán)格弱序的要求是:

兩個(gè)關(guān)鍵字不能同時(shí)“嚴(yán)格弱序”于對(duì)方

如果a“嚴(yán)格弱序”于b,且b“嚴(yán)格弱序”于c,則a必須“嚴(yán)格弱序”于c

如果存在兩個(gè)關(guān)鍵字,任何一個(gè)都不“嚴(yán)格弱序”于另一個(gè),則這兩個(gè)關(guān)鍵字是相等的。

如果是自定義結(jié)構(gòu)用于做有序的關(guān)聯(lián)容器key,那么應(yīng)該重載<運(yùn)算符或者定義一個(gè)比大小的仿函數(shù),其邏輯需要遵守“嚴(yán)格弱序”規(guī)則。如果自定義結(jié)構(gòu)不遵守嚴(yán)格弱序規(guī)則,那么會(huì)導(dǎo)致無法查找到指定key的問題。

對(duì)于無序的關(guān)聯(lián)容器,如果要將自定義類型作為unordered_map的鍵值,需如下兩個(gè)步驟:

定義哈希函數(shù)的函數(shù)對(duì)象;

定義等比函數(shù)的函數(shù)對(duì)象或者在自定義類里重載operator==()

參照:C++ STL:unordered_map 自定義鍵值類型

15.智能指針,

基于RAII原理,幫助程序員管理堆內(nèi)存,避免內(nèi)存泄漏。C++11包含unique_ptr、shared_ptr和weak_ptr三種智能指針,已經(jīng)棄用auto_ptr指針。unique_ptr獨(dú)占對(duì)象,支持move語義,shared_ptr通過引用計(jì)數(shù),來共享對(duì)象,當(dāng)引用計(jì)數(shù)為0時(shí),銷毀對(duì)象,weak_ptr通常配合shared_ptr使用,其不會(huì)增加對(duì)象的引用計(jì)數(shù),可以解決環(huán)狀引用問題。

16.const用法,

const可以用來定義常量,常量必須在定義的時(shí)候完成初始化。

const修飾變量或者函數(shù)的時(shí)候,可以從右向左來查看修飾的目標(biāo),比如const位于函數(shù)的右側(cè),那么就是修飾一個(gè)函數(shù),const位于指針的右側(cè),那么就是修飾一個(gè)指針,const位于指針的左側(cè),那么修飾的就是指針指向的對(duì)象,const位于引用的左側(cè),那么修飾的就是引用的對(duì)象。

const成員變量,只能在構(gòu)造函數(shù)的初始化列表中進(jìn)行初始化,這也就意味著const成員變量是與對(duì)象綁定的。

const對(duì)象,只能調(diào)用const成員函數(shù)。

const成員函數(shù),可以理解為是對(duì)*this對(duì)象進(jìn)行了const修飾,使得無法對(duì)成員變量進(jìn)行修改操作。

17.template,

模板是可以遞歸調(diào)用的,能實(shí)現(xiàn)在編譯期完成計(jì)算,經(jīng)典的求累加代碼:

template <int n>

class Fac

{

public:

? ? enum { sum = Fac<n-1>::sum + n };

};

template <>

class Fac<1>

{

public:

? ? enum { sum = 1 };

};

18.進(jìn)程間通信,

有5種方式:管道(包括無名管道pipe和有名管道fifo)、消息隊(duì)列、信號(hào)量、共享內(nèi)存以及socket。

使用消息隊(duì)列,需要經(jīng)歷內(nèi)核與用戶空間的拷貝,1是用戶空間到內(nèi)核,2是內(nèi)核到內(nèi)存,3是內(nèi)存到內(nèi)核,4是內(nèi)核到用戶空間。

使用共享內(nèi)存,不需要經(jīng)歷內(nèi)核與用戶空間的拷貝,1是用戶空間到內(nèi)存,2是內(nèi)存到用戶空間。

mmap和shmget都可以實(shí)現(xiàn)共享內(nèi)存,mmap是把文件物理地址映射到進(jìn)程的虛擬地址空間中,對(duì)文件的操作與跟內(nèi)存的操作一樣,這樣機(jī)器重啟不會(huì)導(dǎo)致內(nèi)容丟失,而且可以保存很大的數(shù)據(jù)。shmget是在內(nèi)存中開辟出來的一塊共享空間,機(jī)器重啟后數(shù)據(jù)就丟失了。

mmap只從物理磁盤加載使用到的部分?jǐn)?shù)據(jù)到內(nèi)存中操作,而且系統(tǒng)會(huì)自動(dòng)將臟數(shù)據(jù)寫回磁盤,因此可以不受限于內(nèi)存大小,來快速操作很大的文件內(nèi)容。

19.虛擬內(nèi)存,物理內(nèi)存,共享內(nèi)存

top中對(duì)應(yīng)VIRT,RES,SHR。

VIRT表示程序運(yùn)行中需要的總內(nèi)存大小,這部分內(nèi)存不會(huì)一次性全部映射到物理內(nèi)存,只有被頻繁訪問的數(shù)據(jù)才會(huì)進(jìn)入物理內(nèi)存。如果VIRT一直變大,可能有內(nèi)存泄漏發(fā)生。

RES是程序?qū)嶋H占用的物理內(nèi)存大小,也就是常駐內(nèi)存(RSS),其在運(yùn)行中可能會(huì)逐漸增大或縮小??匆粋€(gè)程序?qū)嶋H占用的內(nèi)存大小,一般看RES。

SHR是程序共享的各種動(dòng)態(tài)庫占用的內(nèi)存大小,每個(gè)庫在內(nèi)存中只保存一份,多個(gè)調(diào)用的程序會(huì)共用這些內(nèi)存。

swap是在磁盤上開辟的一塊空間,用于在物理內(nèi)存不足的時(shí)候,操作系統(tǒng)將一部分內(nèi)存數(shù)據(jù)交換到swap區(qū),如果出現(xiàn)頻繁的swap換入換出,帶來大量磁盤操作,會(huì)使得系統(tǒng)性能下降。通??截惔笪募臅r(shí)候,可能會(huì)引起swap交換。

20.fork原理

父進(jìn)程通過fork函數(shù)創(chuàng)建子進(jìn)程,子進(jìn)程擁有新的進(jìn)程號(hào),父子進(jìn)程擁有各自獨(dú)立的虛擬地址空間,但是共享相同的物理空間,只有當(dāng)一方對(duì)共享數(shù)據(jù)進(jìn)行修改時(shí),才根據(jù)copy on write機(jī)制,先拷貝一份,然后在拷貝的地址上做修改。

21.文件的打開與關(guān)閉原理,各種操作文件函數(shù)對(duì)比,為何需要關(guān)閉打開的文件?


22.磁盤管理


23.core文件


24.程序是如何生成的?

源代碼需要經(jīng)過預(yù)編譯處理、編譯、匯編、鏈接等4個(gè)步驟來生成一個(gè)可執(zhí)行文件。

預(yù)編譯處理生成.i文件,是將一些include引用的代碼加入到代碼中,去掉注釋,展開宏,處理?xiàng)l件編譯指令,添加行號(hào)等。

編譯步驟會(huì)生成.S文件,將源代碼轉(zhuǎn)換為匯編代碼,這個(gè)過程會(huì)進(jìn)行詞法分析、語法分析、語義分析和編譯優(yōu)化。

匯編步驟會(huì)生成.o文件,將匯編代碼轉(zhuǎn)換為二進(jìn)制代碼。

鏈接步驟會(huì)將各種.o文件鏈接起來,生成可執(zhí)行文件,包含靜態(tài)鏈接和動(dòng)態(tài)鏈接兩種方式。

25.程序是如何執(zhí)行的?

26.靜態(tài)鏈接與動(dòng)態(tài)鏈接

27.內(nèi)存泄漏

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,235評(píng)論 0 38
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,621評(píng)論 1 32
  • 題目類型 a.C++與C差異(1-18) 1.C和C++中struct有什么區(qū)別? C沒有Protection行為...
    阿面a閱讀 7,885評(píng)論 0 10
  • 最全的iOS面試題及答案 iOS面試小貼士 ———————————————回答好下面的足夠了-----------...
    zweic閱讀 2,796評(píng)論 0 73
  • 堅(jiān)持不是一件容易的事,但是想要堅(jiān)持下去卻不難。一個(gè)人用心想要去做的事,無論過程中遇到什么,那些苦難都會(huì)被自動(dòng)屏蔽,...
    依諾2008閱讀 40評(píng)論 0 0

友情鏈接更多精彩內(nèi)容