C++面试知识点汇总
暫時存?zhèn)€檔,可能有不足之處,歡迎指正。
C++ 基礎(chǔ)
-
new ,delete,new[],delete[],malloc,free之間的區(qū)別?
- new,delete與new[],delete[]區(qū)別在于前者管理的是單個元素空間,后者管理的是多個元素的連續(xù)空間;
- …pass
-
什么是內(nèi)存泄漏,如何避免?如何檢測?
-
定義:內(nèi)存泄漏是由于疏忽或錯誤程序未能釋放 不再使用的內(nèi)存的情況。
-
內(nèi)存泄漏場景:
- 指針重新賦值時,忘記釋放之前管理的內(nèi)存;
- 結(jié)構(gòu)化對象成員中有動態(tài)分配內(nèi)存塊,而卻先釋放了父塊,導(dǎo)致丟失了子塊控制權(quán);
- 返回的是堆區(qū)域內(nèi)存的指針,但是使用后沒有正確處理返回的指針;
-
-
避免:
- 盡量避免在堆上分配內(nèi)存;
- 善于利用RAII機制;
- 盡量不要使用裸指針,而是使用智能指針;
- 盡量使用STL容器替代,比如用string代替char*,vector代替int*等;
-
檢測:
- 對象計數(shù),構(gòu)造++,析構(gòu)–,看最終計數(shù)是否為0;
? 2.使用檢測庫或檢測工具,庫:比如win:crtdbg,linux:mcheck,工具:比如gperftools;
win32 一個進(jìn)程最多可以開幾個線程,開多了會出現(xiàn)什么問題?
- 2^32=4GB,win32最大可用虛擬空間為2GB = 2^31左右,一個線程要1MB棧空間
- 8bit=1byte,1kb = 1024byte=2^10*8bit,1MB = 1024kb=2^20*8bit,1GB = 1024MB
- 1MB * 1024 * 2 = 2GB,所以最多可以開2048個線程
- 開多了會導(dǎo)致整個進(jìn)程異常退出
-
出現(xiàn)野指針有哪些情況,如何避免?
-
野指針定義:指向非法內(nèi)存地址的的指針,也叫懸掛指針;
-
出現(xiàn)情況:
- 使用未初始化的指針;
- 指針指向?qū)ο笠呀?jīng)消亡;
- 指針釋放后未置空;
-
-
避免:
- 盡量避免使用指針,比如用引用代替;
- 定義時就初始化,初始化為一個對象地址或者nullptr;
- 對指針進(jìn)行delete 或free后,將其賦值為nullptr;
指針相關(guān):
- int(*p)[n]:()優(yōu)先級高,所以p是一個指針,指向int[n]的指針,也稱行指針;
行指針++就跨一行 - int* p[n]:指針數(shù)組,含有n個指針元素,常用來指向二維數(shù)組,
p[i][j] = *(p[i]+j)=*(*(p+i)+j)=*(p+i)[j]
c++ lambda表達(dá)式實現(xiàn)原理:
- 根據(jù)lambda表達(dá)式返回值為一個函數(shù)對象可知其實現(xiàn)為:生成一個lambda_xx 類,然后重載operator()
- 具體步驟為:
- 創(chuàng)建 lambda 類,實現(xiàn)構(gòu)造函數(shù),使用 lambda 表達(dá)式的函數(shù)體重載 operator()( lambda 表達(dá)式 也叫匿名函數(shù)對象)
- 創(chuàng)建 lambda 對象
- 通過對象調(diào)用 operator()
編譯器如何區(qū)分重載函數(shù)的?
編譯成了不同的函數(shù)名,查看**生成的匯編代碼(g++ -S xx.s xx.cpp )**發(fā)現(xiàn),
大概規(guī)則舉例:print(int x)->_xxprinti,print(int x,int y)->__xprintii
C++ 的多態(tài)機制?
- 靜態(tài)多態(tài):函數(shù)重載,泛型編程(函數(shù)模板和類模板);
- 動態(tài)多態(tài):虛函數(shù)
什么是仿函數(shù)?
-
通過重載operator()運算符使得class或struct對象可以像函數(shù)一樣調(diào)用,比如c++提供的less,greater等;
-
函數(shù)指針雖然也可以將函數(shù)作為參數(shù)傳遞,但是不能滿足STL抽象性和積木性(和STL其他組件搭配)要求。
-
調(diào)用方式:class_name()(args) or class_obj.operator()(args)
如果new的東西,用free釋放會怎么樣?free后再調(diào)用delete會怎么樣?
-
如果new對象里面沒有再動態(tài)分配資源,則直接free也可以,不會有資源泄漏;
否則會存在資源泄漏,因為對象回收資源的的析構(gòu)函數(shù)沒有被調(diào)用;
-
free之后再調(diào)用delete會free兩次,程序直接Aborted;
自己實現(xiàn)一個String類
class String{public:String(const char* str=nullptr){m_size = str ? std::strlen(str) : 0;m_data = new char[m_size+1];if(m_size) std::strncpy(m_data,str,m_size);m_data[m_size] = '\0'; // 得分}String(const String& s){size_t len = s.size();this->m_data = new char[len+1];std::strncpy(this->m_data,s.m_data,len);m_size = len;this->m_data[m_size] = '\0';}String& operator= (const String& s){if(this == &s) return *this; // 得分this->m_size = s.size();delete[] m_data; // 得分this->m_data = new char[this->m_size];std::strncpy(this->m_data,s.m_data,m_size);this->m_data[m_size] = '\0';return *this;}String& operator+ (const String& s){int len = s.size();char* q = new char[this->m_size+len];std::strncpy(q,m_data,m_size);std::strncpy(q+m_size,s.m_data,len);this->m_size += len;q[m_size] = '\0';delete[] m_data; // 得分m_data = q;return *this;}friend std::ostream& operator<< (std::ostream& out,String& s){for(char* q = s.m_data; *q!='\0'; ++q){out<<(*q);}return out;}size_t size()const{return m_size;}~String(){delete[] m_data;}private:char* m_data;size_t m_size; };自己實現(xiàn)單例模式
// 懶漢模式:需要用時再加載(創(chuàng)建) class Object{public:static Object* getInstance(){if(nullptr == m_single){ // 只有m_single為空才加鎖,加鎖開銷很大,避免每次調(diào)用都加鎖std::unique_lock<std::mutex> lock(m_mutex);if(nullptr == m_single){m_single = new Object();}} return m_single;}private:Object(){}Object(const Object& rhs) = delete; // 禁止拷貝構(gòu)造Object& operator=(const Object& rhs) = delete; // 禁止賦值構(gòu)造static Object* m_single;std::mutex m_mutex;}; // 餓漢模式: 一開始就創(chuàng)建好實例 class Object{public:static Object* getInstance(){static Object m_single; // 一開始就創(chuàng)建好放到靜態(tài)存儲區(qū),在c++11下線程安全return &m_single;}private:Object(){}Object(const Object& rhs) = delete; // 禁止拷貝構(gòu)造Object& operator=(const Object& rhs) = delete; // 禁止賦值構(gòu)造 };單例模式下,如何保證多線程模式下只有一個對象實例?
- 加鎖
- 提前生成,即使用餓漢模式
char ch[] = “hello” 和char* p = "hello"的區(qū)別,如果對兩者字符進(jìn)行修改會怎么樣?
-
char ch[]最后有一個字符串結(jié)束符‘\0’
-
sizeof(ch) = strlen(ch)+1而sizeof(p) = 8 or 4
-
char ch[]中字符串可以修改不會出現(xiàn)問題,而對p指向的字符串修改可能會出現(xiàn)問題,
ch存在棧區(qū),p指向的字符串在常量區(qū)
為什么要字節(jié)對齊?字節(jié)不對齊會出現(xiàn)什么問題?
- 提高訪問效率,不對齊讀取數(shù)據(jù)時可能需要多訪問內(nèi)存幾次
為什么x64 下指針占8字節(jié),x64的尋址空間?
- x64地址總線有64條,尋址空間2^64,指針作用是指向一個地址(尋址空間中找一個地址),64bit = 8Bytes,
memset有虛函數(shù)的對象為0,會不會出現(xiàn)問題?
- 會,虛表指針被置空,導(dǎo)致無法調(diào)用到虛函數(shù)
簡述mp[1] 和 iter = mp.find(1)的區(qū)別,map的key有什么要求?
-
下標(biāo)操作符[],1不在mp中時會插入,而find(1),1不在時不會插入,只返回mp.end();
所以如果只是判斷某個數(shù)是否在mp中時盡量使用find
-
map的key需要:
- 支持拷貝構(gòu)造;
- 支持operator=;
- 可比較,即支持operator< (如果沒有operator<那么 map模板必須增加第三個模板參數(shù));
- 默認(rèn)的構(gòu)造函數(shù)。
map插入,查找時間復(fù)雜度?為什么map底層采用紅黑樹而不用普通AVL? 紅黑樹插入和刪除最多需要幾次旋轉(zhuǎn)?
-
logn
-
map要做頻繁插入,由于普通AVL嚴(yán)格的平衡,一次插入需要調(diào)整需要很多次,而紅黑樹一次插入最多需要調(diào)整2次;
-
插:最多調(diào)整2次,刪:最多調(diào)整3次;
C++中死鎖的危害很大,對避免死鎖有什么建議?
-
避免多次鎖定:盡量避免同一線程對多個鎖進(jìn)行鎖定;
-
按相同的順序加鎖;
-
使用定時鎖(破壞持有并等待條件);
-
提前進(jìn)行死鎖檢測(銀行家算法);
class A{…virtual… }, class B :A{…},B的內(nèi)存布局是怎么樣的?
虛函數(shù)有什么缺點?作為基類,其析構(gòu)函數(shù)一定要聲明為virtual?
-
時間:調(diào)用需要通過虛表指針查虛表,時間上開銷更大;
-
空間:由于有虛函數(shù),所以類對象會有虛表指針,要更多內(nèi)存開銷;
-
安全:將基類強制轉(zhuǎn)換為子類,不可以直接訪問子類特有的虛函數(shù),可通過虛表指針間接訪問;
不一定,如果不需要動態(tài)管理資源則基類析構(gòu)可不聲明為virtual
虛函數(shù)和普通函數(shù)的區(qū)別?
- 虛函數(shù)動態(tài)編譯,運行期間進(jìn)行綁定;而普通函數(shù)靜態(tài)編譯,在運行前就綁定好了(通過this指針進(jìn)行綁定)。
簡述幾種xx_cast的作用。
-
reinterpret_cast:重新解釋二進(jìn)制碼,風(fēng)險很高,比如不同類型指針之間的轉(zhuǎn)換,int與指針的轉(zhuǎn)換等;
-
const_cast:const 轉(zhuǎn)為非const,volitale 轉(zhuǎn)為非volitale
(volitale表明所修飾變量是易變的,不要作讀取優(yōu)化,每次都重新從內(nèi)存中取);
-
static_cast:相當(dāng)于傳統(tǒng)C語言的強制轉(zhuǎn)換,非多態(tài)的轉(zhuǎn)換,編譯時檢查,
但運行時沒有安全檢查,所以子類->父類絕對安全,而父類->子類是不安全的。
不能轉(zhuǎn)掉const,volitale,即不可將含這些關(guān)鍵字的轉(zhuǎn)換為不含這些關(guān)鍵字的類型;
-
dynamic_cast:主要用途將父類指針或引用安全的轉(zhuǎn)換為子類指針或引用,它在運行期間借助RAII進(jìn)行類型轉(zhuǎn)換,
強制轉(zhuǎn)換指針不成功返回空指針,強制轉(zhuǎn)換引用不成功拋出異常
你了解服務(wù)器的哪些架構(gòu)?
引用是如何實現(xiàn)?何時使用引用?(引用和指針的區(qū)別)
-
底層實現(xiàn):可以根據(jù)使用場景進(jìn)行推斷,聲明后必須初始化且之后不可修改為其他的引用,
根據(jù)這個特性可以推斷底層實現(xiàn)是常指針(type* const),
但sizeof(引用)是所引用對象的大小(引用是對象的別名);
-
應(yīng)用: 函數(shù)傳參,減少大數(shù)據(jù)結(jié)構(gòu)的拷貝;為什么要有引用?很多場合可以代替指針,提高可讀性和實用性;
說說C++的多態(tài)
- 靜態(tài)多態(tài):重載(形參類型,個數(shù),和返回值類型沒有關(guān)系),泛型編程(類模板,模板函數(shù))
- 動態(tài)多態(tài):虛函數(shù)
虛函數(shù),一個類可以有多個虛函數(shù)表嗎?
- 可以,對于多繼承可能存在多個虛函數(shù)表(多個虛表指針)
說一下程序的編譯成可執(zhí)行文件的過程
- 預(yù)處理(替換宏定義等),編譯(高級語言轉(zhuǎn)換為匯編代碼)匯編(匯編碼轉(zhuǎn)換為機器碼),鏈接
C++ STL構(gòu)成:
- 容器
- 迭代器
- 適配器
- 分配器
- 算法
- 函數(shù)對象(仿函數(shù))
你常用的stl容器,vector的擴(kuò)容機制,清除里面的內(nèi)容內(nèi)存會釋放嗎?
-
stl容器分為順序容器:
-
vector:擴(kuò)容機制根據(jù)實現(xiàn)而異,通常為2倍/1.5倍。clear不會釋放內(nèi)存;
-
deque,
-
list(雙向鏈表),
-
forward_list(單向鏈表),
-
array,
-
string
-
-
關(guān)聯(lián)容器:
- map
- set
- multimap
- multiset
- unordered_map
- unordered_set
- unordered_multimap
- unordered_multiset
stack屬于容器適配器;
map和unordered_map的區(qū)別,以及它們的查詢復(fù)雜度:
- map底層紅黑樹(比常規(guī)AVL更適合插入修改密集型任務(wù)),查詢時間復(fù)雜度O(NlogN);
- unordered_map底層采用hash,查詢時間復(fù)雜度為常數(shù)復(fù)雜度
虛函數(shù)和普通函數(shù)的區(qū)別:
- 虛函數(shù)運行時多態(tài),而普通函數(shù)是靜態(tài)編譯,沒有運行時多態(tài);
計算機網(wǎng)絡(luò)
-
tcp如何保證可靠性?
-
校驗和:由tcp偽首部(12byte),tcp首部,tcp數(shù)據(jù) 三部分組成;
-
序列號和確認(rèn)應(yīng)答;
-
超時重傳:時限略大于1個RTT(往返時間);
-
流量控制:滑動窗口機制;
-
擁塞控制:利用擁塞窗口以不加重網(wǎng)絡(luò)負(fù)擔(dān):慢啟動(之后指數(shù)增大),擁塞避免(之后線性增大),快重傳,快恢復(fù);
-
連接管理:三次握手,四次揮手;
-
-
簡述tcp/ip模型,并說明各層常用的協(xié)議
osi七層:
- 應(yīng)用層:
- 表示層:
- 會話層:
- 傳輸層:
- 網(wǎng)絡(luò)層:
- 數(shù)據(jù)鏈路層:
- 物理層:
tcp/ip:
- 該模型意味著tcp和ip協(xié)同工作;
- tcp協(xié)議在傳輸層,負(fù)責(zé)應(yīng)用軟件和網(wǎng)絡(luò)軟件之間的通信;
- ip協(xié)議在網(wǎng)絡(luò)層,負(fù)責(zé)計算機之間的通信;
tcp/ip模型4層:
-
應(yīng)用層(osi前三層組合):HTTP,FTP,DNS
-
傳輸層:TCP,UDP
-
網(wǎng)絡(luò)層:ARP,ICMP
-
網(wǎng)絡(luò)接口層(osi最后兩層組合):MAC,VLAN
-
ip地址(最小)占幾個字節(jié)
- ipv4由32為二進(jìn)制表示,每32/8 = 4bytes
-
accept發(fā)生在三次握手哪個階段?
- 第三次握手階段accept返回
-
tcp和udp的區(qū)別,還有應(yīng)用場景:
-
tcp面向連接的,面向字節(jié)流的,可靠的,點對點,有流量控制和擁塞控制(慢啟動,擁塞避免,快重傳,快恢復(fù)),
首部20字節(jié);應(yīng)用:文件傳輸,重要狀態(tài)更新;
-
udp是面向數(shù)據(jù)報,不可靠的,支持一對一,多對多,一對多,沒有擁塞控制,首部開銷8字節(jié);應(yīng)用:視頻傳輸,
實時通信;
-
-
tcp的四次揮手過程?
- pass
-
了解tcp的滑動窗口?
窗口:無需確認(rèn)包的情況下一次性可以發(fā)送的最大數(shù)據(jù)包數(shù)量;
滑動窗口機制用于流量控制,有發(fā)送窗口和接收窗口,初始窗口大小根據(jù)帶寬而定,后面根據(jù)收發(fā)情況動態(tài)調(diào)整窗口大小
數(shù)據(jù)結(jié)構(gòu)和算法
-
如何判斷一點在線段的左邊還是右邊或者線上,如果在左邊返回true,否則返回false.
-
計算向量之間叉積:
- 大于0 : 左側(cè)
- 小于0:右側(cè)
- 等于0:線上
-
-
字符串匹配(手寫KMP,并分析復(fù)雜度)
// 時間復(fù)雜度: O(M+N) void getNext(const char* p,int* next){// 模式串p(需要匹配的)int i = 1;int j = 0;int len = strlen(p);next[0] = 0;while(i < len){if(p[i] == p[j]){next[i++] = ++j;}else{j > 0 ? j = next[j-1] : next[i++] = 0;}} } int kmp(const char* s,const char* p){// s中找pint i = 0;int j = 0;int lens = strlen(s);int lenp = strlen(p);while(i < lens){if(s[i] == p[j]){++i;++j;}else{j > 0 ? j = next[j-1] : i++;}if(j == lenp){return i-lenp; //匹配一個// 若多個: j = next[j-1];}}return j == lenp ? (i-lenp) : -1; } -
快排思路
-
對排序的穩(wěn)定性是如何理解? 快排穩(wěn)定嗎?
-
進(jìn)制轉(zhuǎn)換:10->8,10->7
- 除余法
-
刪除鏈表中指定元素(需要避免內(nèi)存泄漏)
-
遍歷,同時記錄前驅(qū)結(jié)點pre,遇到目標(biāo)元素now,pre->next=now->next,delete 結(jié)點時需要注意;
struc Node{Node():val(0),next(nullptr){}int val;Node* next; }; Node* deleteNode(Node* head,int val){Node* new_head = new Node();new_head->next = head;Node* pre = new_head;Node* now = head;Node* del = nullptr;while(now){if(now->val==val){pre->next = now->next;del = now;now = now->next;delete del;}else{pre = now;now = now->next; }}head = new_head->next;delete new_head;return head; }
-
-
TopK問題以及時間復(fù)雜度
- 快速選擇,O(N)
- 維護(hù)大小為k的堆,O(NlogK)
- 如果有多臺機器,分治法
-
如何解決hash碰撞:
- 鏈地址法
- 開放地址法(線性探測,二次方探測)
- 再hash法
- 建立公共溢出區(qū)(溢出區(qū)再沖突可以結(jié)合其他解決hash碰撞的方法)
操作系統(tǒng)
-
什么是死鎖,舉個簡單例子,死鎖條件?如何避免?
-
定義:多個進(jìn)程因為爭奪資源而造成的一種僵局,各自都無法往下推進(jìn);
比如進(jìn)程A,B,資源C,D,A持有了C還想要D,B持有了D還想要C;
-
條件:
- 互斥
- 不可剝奪
- 持有并保持
- 循環(huán)等待
-
避免:
- 按相同的順序加鎖
- 一定時間內(nèi)沒有獲取到其他鎖,釋放已有的鎖(定時鎖)
- 提前檢測死鎖(銀行家算法)
-
-
簡述消費者和生產(chǎn)者模型和讀寫者模型,并簡述它們的區(qū)別;
-
生產(chǎn)者和消費者一段時間內(nèi)共用一段內(nèi)存(緩沖區(qū)),生:放,消:取;無數(shù)據(jù):消費者阻塞,數(shù)據(jù)滿:生產(chǎn)者阻塞;
-
區(qū)別:
- 讀寫者模型出現(xiàn)了讀者共享的關(guān)系,而生產(chǎn)者和消費者模型只有互斥和同步關(guān)系(生與消:互斥和同步;生和生,消與消:互斥);
- 讀者不會使得緩沖區(qū)改變;
-
線程通信方式,如何保證數(shù)據(jù)一致性?
通信方式:
- 全局變量
- 信號機制
同步:
? 臨界區(qū):通過多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數(shù)據(jù)訪問;
? 互斥量Synchronized/Lock:采用互斥對象機制,只有擁有互斥對象的線程才有訪問公共資源的權(quán)限。因為互斥對象只有一個,所以可以保證公共資源不會被多個線程同時訪問
? 信號量Semphare:為控制具有有限數(shù)量的用戶資源而設(shè)計的,它允許多個線程在同一時刻去訪問同一個資源,但一般需要限制同一時刻訪問此資源的最大線程數(shù)目。
? 事件(信號),Wait/Notify:通過通知操作的方式來保持多線程同步,還可以方便的實現(xiàn)多線程優(yōu)先級的比較操作;
進(jìn)程的通信方式:
-
管道(PIPE和命名管道FIFO)
-
系統(tǒng)IPC(inter-process communication):
- 消息隊列
- 信號量
- 信號
- 共享內(nèi)存
-
Socket通信
線程同步和異步
-
同步:多個線程訪問同一資源,當(dāng)資源互斥時,其他線程盲目等待;
-
異步:多個線性訪問互斥資源時,不盲目等待先去干其他事情;
阻塞和非阻塞
-
阻塞:會掛起線程(盲目)
-
非阻塞:不會掛起線程(不盲目)
數(shù)據(jù)庫
-
什么是主鍵?什么是索引?如何使用索引?索引適用于什么場景?
ref1:【面試】面試常問之?dāng)?shù)據(jù)庫索引
-
主鍵:能夠唯一標(biāo)識表中一條記錄的最小屬性集(候選碼中選一個)
-
索引:是數(shù)據(jù)庫對表中一列或多列進(jìn)行排序的結(jié)構(gòu),便于快速找到數(shù)據(jù);
-
使用:
-
創(chuàng)建:
way1: create index_type index_name on table_name(column_name_tuple)
way2: alter table table_name add index_type index_name(column_name_tuple)
-
刪除:drop index index_name on table_name
-
查詢:show index from table_name
-
-
適用場景:頻繁查詢,較少增加,刪除,修改的情況(因為維護(hù)索引開銷大);
-
索引的種類(ref2:什么是數(shù)據(jù)庫索引):
大類:
-
聚簇索引(將數(shù)據(jù)存儲與索引放到了一塊,一個表只能有一個聚簇索引,聚簇索引默認(rèn)為主鍵):
在數(shù)據(jù)庫中,表中數(shù)據(jù)會按主鍵排序;
-
非聚簇索引(輔助索引):給普通字段加上索引,查找時:類似于先找目錄再找頁碼;
小類:
- 普通索引和唯一索引
- 單值索引和復(fù)合索引
-
-
-
寫sql:
- 不能使用min,查找char表中age最小的記錄
- 查找table(ID,Name)中具有2個以上ID的Name(即一個Name有2+個ID)
- 查找info表中interest中含有football的用戶
- 查找info表中interest中含有football并且age小于30的用戶
- 查找info表中用戶平均年齡
- 查找ipinfo中相同ip中datetime最大的信息
python
- 有哪些方式class 中某個屬性的值?
- 正則匹配
其他
- 你覺得你的優(yōu)勢是什么?優(yōu)點?缺點?
- 你覺得你在哪方面會讓別人眼前一亮?即你擅長什么?
- 實習(xí)遇到什么困難?
總結(jié)
以上是生活随笔為你收集整理的C++面试知识点汇总的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 已知向量坐标求三角形面积
- 下一篇: s3c2440移植MQTT