C++ 面試100題

首先鄭重聲明阔挠,這些面試題的答案都是參考網(wǎng)上的答案和自己理解的部分整合起來镀层,如有錯誤嗜浮,歡迎指針嘉汰。

1 多態(tài)的實現(xiàn)

存在虛函數(shù)的類至少有一個(多繼承會有多個)一維的虛函數(shù)表叫做虛表(virtual table)丹禀,屬于類成員,虛表的元素值是虛函數(shù)的入口地址鞋怀,在編譯時就已經(jīng)為其在數(shù)據(jù)端分配了空間双泪。編譯器另外還為每個類的對象提供一個虛表指針(vptr),指向虛表入口地址密似,屬于對象成員焙矛。在實例化派生類對象時,先實例化基類辛友,將基類的虛表入口地址賦值給基類的虛表指針薄扁,當基類構(gòu)造函數(shù)執(zhí)行完時剪返,再將派生類的虛表入口地址賦值給基類的虛表指針(派生類和基類此時共享一個虛表指針废累,并沒有各自都生成一個),在執(zhí)行父類的構(gòu)造函數(shù)脱盲。
以上是C++多態(tài)的實現(xiàn)過程邑滨,可以得出結(jié)論:

  • 1 有虛函數(shù)的類必存在一個虛表。
  • 2 虛表的構(gòu)建:基類的虛表構(gòu)建钱反,先填上虛析構(gòu)函數(shù)的入口地址掖看,之后所有虛函數(shù)的入口地址按在類中聲明順序填入虛表;派生類的虛表構(gòu)建面哥,先將基類的虛表內(nèi)容復制到派生類虛表中哎壳,如果派生類覆蓋了基類的虛函數(shù),則虛表中對應(yīng)的虛函數(shù)入口地址也會被覆蓋尚卫,為了后面尋址的一致性归榕。

class Person{ 
     . . . 
 public : 
    Person (){} 
    virtual ~Person (){}; 
    virtual void speak (){}; 
    virtual void eat (){}; 
 }; 
 
class Girl : public Person{ 
     . . . 
   public : 
   Girl(){} 
   virtual ~Girl(){}; 
   virtual void speak(){}; 
   virtual void sing(){}; 

虛表構(gòu)建圖

虛函數(shù)表中有序放置了父類和子類中的所有虛函數(shù),并且相同虛函數(shù)在類繼承鏈中的每一個虛函數(shù)表中的偏移量都是一致的吱涉。所以確定的虛函數(shù)對應(yīng)virtual table中一個固定位置n刹泄,n是一個在編譯時期就確定的常量外里,所以,使用vptr加上對應(yīng)的n特石,就可以得到對應(yīng)的函數(shù)入口地址盅蝗。C++采用的這種絕對地址+偏移量的方法調(diào)用虛函數(shù),查找速度快執(zhí)行效率高姆蘸,時間復雜度為O(1)
這里概括一下虛函數(shù)的尋址過程:

1墩莫、獲取類型名和函數(shù)名

2、從符號表中獲得當前虛函數(shù)的偏移量

3逞敷、利用偏移量得到虛函數(shù)的訪問地址贼穆,并調(diào)用虛函數(shù)。vptrn

2  C/C++的區(qū)別

C面向過程兰粉,C++面向?qū)ο蠊嗜++幾乎是C的一個超集,幾乎包含了C玖姑。

3 const 關(guān)鍵字

常變量: const 類型說明符 變量名

常引用: const 類型說明符 &引用名

常對象: 類名 const 對象名

常成員函數(shù): 類名::fun(形參) const

常數(shù)組: 類型說明符 const 數(shù)組名[大小]

常指針: const 類型說明符* 指針名 愕秫,類型說明符* const 指針名

用法1:常量
取代了C中的宏定義,聲明時必須進行初始化(!c++類中則不然)焰络。const限制了常量的使用方式戴甩,并沒有描述常量應(yīng)該如何分配。如果編譯器知道了某const的所有使用闪彼,它甚至可以不為該const分配空間甜孤。最簡單的常見情況就是常量的值在編譯時已知,而且不需要分配存儲畏腕。―《C++ Program Language》
 用const聲明的變量雖然增加了分配空間缴川,但是可以保證類型安全
用法2:指針和常量
使用指針時涉及到兩個對象:該指針本身和被它所指的對象。將一個指針的聲明用const“預先固定”將使那個對象而不是使這個指針成為常量描馅。要將指針本身而不是被指對象聲明為常量把夸,必須使用聲明運算符*const。
所以出現(xiàn)在 * 之前的const是作為基礎(chǔ)類型的一部分:
char *const cp; //到char的const指針
char const *pc1; //到const char的指針
const char pc2; //到const char的指針(后兩個聲明是等同的)
從右向左讀的記憶方式:
cp is a const pointer to char. 故pc不能指向別的字符串铭污,但可以修改其指向的字符串的內(nèi)容
pc2 is a pointer to const char. 故
pc2的內(nèi)容不可以改變恋日,但pc2可以指向別的字符串
且注意:允許把非 const 對象的地址賦給指向 const 對象的指針,不允許把一個 const 對象的地址賦給一個普通的、非 const 對象的指針嘹狞。
用法3:const修飾函數(shù)傳入?yún)?shù)
將函數(shù)傳入?yún)?shù)聲明為const岂膳,以指明使用這種參數(shù)僅僅是為了效率的原因,而不是想讓調(diào)用函數(shù)能夠修改對象的值磅网。同理谈截,將指針參數(shù)聲明為const,函數(shù)將不修改由這個參數(shù)所指的對象。
通常修飾指針參數(shù)和引用參數(shù):
void Fun( const A *in); //修飾指針型傳入?yún)?shù)
void Fun(const A &in); //修飾引用型傳入?yún)?shù)
用法4:修飾函數(shù)返回值
可以阻止用戶修改返回值傻盟。返回值也要相應(yīng)的付給一個常量或常指針速蕊。
用法5:const修飾成員函數(shù)(c++特性)
const對象只能訪問const成員函數(shù),而非const對象可以訪問任意的成員函數(shù)娘赴,包括const成員函數(shù)规哲;
const對象的成員是不能修改的,而通過指針維護的對象確實可以修改的诽表;
const成員函數(shù)不可以修改對象的數(shù)據(jù)唉锌,不管對象是否具有const性質(zhì)。編譯時以是否修改成員數(shù)據(jù)為依據(jù)進行檢查竿奏。

4 malloc/free 和new/delete 區(qū)別

相同點:都可用于申請動態(tài)內(nèi)存和釋放內(nèi)存
不同點:
簡單點說袄简,malloc只分配指定大小的堆內(nèi)存空間,而new可以根據(jù)對象類型分配合適的堆內(nèi)存空間泛啸,當然還可以通過重載operator new 自定義內(nèi)存分配策略吨铸,其次還能夠構(gòu)造對象甫窟,free釋放對應(yīng)的堆內(nèi)存空間,delete,先執(zhí)行對象的析構(gòu)函數(shù),在釋放對象所占空間耿戚。
malloc與free是C++/C 語言的標準庫函數(shù)疼进,new/delete 是C++的運算符拍谐。malloc分配時的大小是人為計算的渔呵,返回類型是void*,使用時需要類型轉(zhuǎn)換,而new在分配時荠雕,編譯器能夠根據(jù)對象類型自動計算出大小稳其,返回類型是指向?qū)ο箢愋偷闹羔槪浞庋b了sizeof和類型轉(zhuǎn)換功能炸卑,實際上new分為兩步既鞠,第一步是通過調(diào)用operator new函數(shù)分配一塊合適,原始的矾兜,未命名的內(nèi)存空間损趋,返回類型也是void *,而且operator new可以重載,可以自定義內(nèi)存分配策略椅寺,甚至不做內(nèi)存分配,甚至分配到非內(nèi)存設(shè)備上蒋失,而malloc無能為力返帕,第二步,調(diào)用構(gòu)造函數(shù)構(gòu)造對象篙挽,new將調(diào)用constructor荆萤,而malloc不能;delete將調(diào)用destructor,而free不能

5 指針和引用的區(qū)別

1链韭、引用在創(chuàng)建時必須初始化偏竟,引用到一個有效對象,不是對象敞峭,不占用內(nèi)存空間踊谋;而指針在定義時不必初始化,可以在定義后的任何地方重新賦值旋讹,是對象殖蚕,占用內(nèi)存空間。
2沉迹、指針可以是NULL睦疫,引用不行
3、引用貌似一個對象的小名鞭呕,一旦初始化指向一個對象蛤育,就不能將其他對象重新賦值給該引用,這樣引用和原對象的值都會被更改葫松。

6 C++中堆和棧的區(qū)別

一缨伊、預備知識—程序的內(nèi)存分配
一個由C/C++編譯的程序占用的內(nèi)存分為以下幾個部分
1、棧區(qū)(stack)— 由編譯器自動分配釋放 进宝,存放函數(shù)的參數(shù)值刻坊,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧党晋。
2谭胚、堆區(qū)(heap) — 一般由程序員分配釋放, 若程序員不釋放未玻,程序結(jié)束時可能由OS回收注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事灾而,分配方式倒是類似于鏈表。
3扳剿、全局區(qū)(靜態(tài)區(qū))(static)—旁趟,全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的 全局變量和靜態(tài)變量在一塊區(qū)域庇绽, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另 一塊區(qū)域锡搜。 - 程序結(jié)束后由系統(tǒng)釋放。
4瞧掺、文字常量區(qū) —常量字符串就是放在這里的耕餐。程序結(jié)束后由系統(tǒng)釋放
5、程序代碼區(qū)—存放函數(shù)體的二進制代碼辟狈。

二肠缔、例子程序
這是一個前輩寫的夏跷,非常詳細
//main.cpp
int a = 0; 全局初始化區(qū)
char *p1; 全局未初始化區(qū)
main()
{
int b; 棧
char s[] = "abc"; 棧
char *p2; 棧
char *p3 = "123456"; 123456/0在常量區(qū),p3在棧上明未。
static int c =0槽华; 全局(靜態(tài))初始化區(qū)
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。
strcpy(p1, "123456"); 123456/0放在常量區(qū)趟妥,編譯器可能會將它與p3所指向的"123456"
優(yōu)化成一個地方猫态。
}

二、堆和棧的理論知識
2.1申請方式
stack:
由系統(tǒng)自動分配煮纵。 例如懂鸵,聲明在函數(shù)中一個局部變量 int b; 系統(tǒng)自動在棧中為b開辟空間
heap:
需要程序員自己申請,并指明大小行疏,在c中malloc函數(shù)
如p1 = (char *)malloc(10);
在C++中用new運算符
如p2 = new char[10];
但是注意p1匆光、p2本身是在棧中的。

2.2
申請后系統(tǒng)的響應(yīng)
棧:只要棧的剩余空間大于所申請空間酿联,系統(tǒng)將為程序提供內(nèi)存终息,否則將報異常提示棧溢出。
堆:首先應(yīng)該知道操作系統(tǒng)有一個記錄空閑內(nèi)存地址的鏈表贞让,當系統(tǒng)收到程序的申請時周崭,會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結(jié)點喳张,然后將該結(jié)點從空閑結(jié)點鏈表中刪除续镇,并將該結(jié)點的空間分配給程序,另外销部,對于大多數(shù)系統(tǒng)摸航,會在這塊內(nèi)存空間中的首地址處記錄本次分配的大小,這樣舅桩,代碼中的delete語句才能正確的釋放本內(nèi)存空間酱虎,另外,由于找到的堆結(jié)點的大小不一定正好等于申請的大小擂涛,系統(tǒng)會自動的將多余的那部分重新放入空閑鏈表中读串。

2.3申請大小的限制
棧:在Windows下,棧是向低地址擴展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域撒妈。這句話的意 思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預先規(guī)定好的恢暖,在WINDOWS下,棧的大小是2M(也有的說是1M踩身,總之是一個編譯時就確定的常數(shù))胀茵,如果申請的空間超過棧的剩余空間時,將提示overflow挟阻。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴展的數(shù)據(jù)結(jié)構(gòu)附鸽,是不連續(xù)的內(nèi)存區(qū)域脱拼。這是由于系統(tǒng)是用鏈表來存儲
的空閑內(nèi)存地址的,自然是不連續(xù)的坷备,而鏈表的遍歷方向是由低地址向高地址熄浓。堆的大小
受限于計算機系統(tǒng)中有效的虛擬內(nèi)存。由此可見省撑,堆獲得的空間比較靈活赌蔑,也比較大。

2.4申請效率的比較:
棧由系統(tǒng)自動分配竟秫,速度較快娃惯。但程序員是無法控制的。
堆是由new分配的內(nèi)存肥败,一般速度比較慢趾浅,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便.
另外,在WINDOWS下馒稍,最好的方式是用VirtualAlloc分配內(nèi)存皿哨,他不是在堆,也不是在棧是
直接在進程的地址空間中保留一塊內(nèi)存纽谒,雖然用起來最不方便证膨。但是速度快,也最靈活鼓黔。

2.5堆和棧中的存儲內(nèi)容
棧: 在函數(shù)調(diào)用時央勒,第一個進棧的是主函數(shù)中后的下一條指令(函數(shù)調(diào)用語句的下一條可
執(zhí)行語句)的地址,然后是函數(shù)的各個參數(shù)请祖,在大多數(shù)的C編譯器中订歪,參數(shù)是由右往左入棧
的,然后是函數(shù)中的局部變量肆捕。注意靜態(tài)變量是不入棧的刷晋。
當本次函數(shù)調(diào)用結(jié)束后,局部變量先出棧慎陵,然后是參數(shù)眼虱,最后棧頂指針指向最開始存的地
址,也就是主函數(shù)中的下一條指令席纽,程序由該點繼續(xù)運行捏悬。
堆:一般是在堆的頭部用一個字節(jié)存放堆的大小。堆中的具體內(nèi)容由程序員安排润梯。

2.6存取效率的比較

char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在運行時刻賦值的过牙;
而bbbbbbbbbbb是在編譯時就確定的甥厦;
但是,在以后的存取中寇钉,在棧上的數(shù)組比指針所指向的字符串(例如堆)快刀疙。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
對應(yīng)的匯編代碼
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到
edx中扫倡,再根據(jù)edx讀取字符谦秧,顯然慢了。


  • 管理方式不同
    棧是編譯器管理撵溃,堆的占用和釋放都是由程序員進行控制的疚鲤;
  • 空間大小不同
    在32位系統(tǒng)下,一般堆的內(nèi)存可以達到4G的空間缘挑,可以說堆內(nèi)存幾乎是沒有限制的集歇。但是對于棧,一般都有一定空間大小(跟編譯器有關(guān))卖哎,比如在VC6下默認的椆碛疲空間大小是1M
  • 能否產(chǎn)生碎片不同
    對于堆來說,頻繁的new/delete操作會造成內(nèi)存空間的不連續(xù)亏娜,從而造成大量碎片焕窝,使程序效率降低;
    但是對于棧來說维贺,因為總是先進后出不存在內(nèi)存塊不連續(xù)的問題它掂。
  • 生長方向不同
    堆的生長方向是向上的,即向著內(nèi)存地址增加的方向溯泣;
    棧的生長方向是向下的虐秋,即向著內(nèi)存地址減小的方向增長。
  • 分配方式不同
    堆總是動態(tài)分配的垃沦,需要程序員手動釋放客给;
    棧存在靜態(tài)分配和動態(tài)分配的:
    其中靜態(tài)分配是由編譯器完成的(比如局部變量的分配);
    動態(tài)分配是由alloca函數(shù)進行分配的(這個函數(shù)會在棧幀的調(diào)用處上分配一定空間肢簿,當調(diào)用alloca的函數(shù)返回到調(diào)用位置時靶剑,這些臨時空間會被自動釋放),棧的動態(tài)分配是由編譯器自己進行釋放的池充。
  • 分配效率不同:
    棧是機器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu)桩引,計算機會在底層對棧提供支持,包括:分配專門的寄存器來存放棧的地址收夸、入出棧都有專門指令坑匠,因此棧的效率會比較高。
    堆是C/C++函數(shù)庫提供的卧惜,其機制非常復雜厘灼,比如為了分配一塊內(nèi)存夹纫,庫函數(shù)會按照一定的算法在堆內(nèi)存中搜索可用的足夠大小的空間,如果找不到(可能是因為內(nèi)存碎片過多)手幢,就可能調(diào)用系統(tǒng)功能區(qū)(用戶模式和內(nèi)核模式的切換)增加程序數(shù)據(jù)段的內(nèi)存空間捷凄,如此便有機會分到足夠大小的內(nèi)存忱详,然后進行返回围来。

7 關(guān)鍵字static

C++的static有兩種用法:面向過程程序設(shè)計中的static和面向?qū)ο蟪绦蛟O(shè)計中的static。前者應(yīng)用于普通變量和函數(shù)匈睁,不涉及類监透;后者主要說明static在類中的作用。

1.面向過程設(shè)計中的static

1.1靜態(tài)全局變量

靜態(tài)全局變量有以下特點:
? 該變量在全局數(shù)據(jù)區(qū)分配內(nèi)存航唆;
? 未經(jīng)初始化的靜態(tài)全局變量會被程序自動初始化為0(自動變量的值是隨機的胀蛮,除非它被顯式初始化);
? 靜態(tài)全局變量在聲明它的整個文件都是可見的糯钙,而在文件之外是不可見的粪狼;

1.2靜態(tài)局部變量

靜態(tài)局部變量有以下特點:
? 該變量在全局數(shù)據(jù)區(qū)分配內(nèi)存;
? 靜態(tài)局部變量在程序執(zhí)行到該對象的聲明處時被首次初始化任岸,即以后的函數(shù)調(diào)用不再進行初始化再榄;
? 靜態(tài)局部變量一般在聲明處初始化,如果沒有顯式初始化享潜,會被程序自動初始化為0困鸥;
? 它始終駐留在全局數(shù)據(jù)區(qū),直到程序運行結(jié)束剑按。但其作用域為局部作用域疾就,當定義它的函數(shù)或語句塊結(jié)束時,其作用域隨之結(jié)束艺蝴;

1.3靜態(tài)函數(shù)

定義靜態(tài)函數(shù)的好處:
? 靜態(tài)函數(shù)不能被其它文件所用猬腰;
? 其它文件中可以定義相同名字的函數(shù),不會發(fā)生沖突


二猜敢、面向?qū)ο蟮膕tatic關(guān)鍵字(類中的static關(guān)鍵字)

2.1靜態(tài)數(shù)據(jù)成員

靜態(tài)數(shù)據(jù)成員有以下特點:
? 對于非靜態(tài)數(shù)據(jù)成員姑荷,每個類對象都有自己的拷貝。而靜態(tài)數(shù)據(jù)成員被當作是類的成員锣枝。無論這個類的對象被定義了多少個厢拭,靜態(tài)數(shù)據(jù)成員在程序中也只有一份拷貝,由該類型的所有對象共享訪問;
? 靜態(tài)數(shù)據(jù)成員存儲在全局數(shù)據(jù)區(qū)撇叁。靜態(tài)數(shù)據(jù)成員定義時要分配空間供鸠,所以不能在類聲明中定義;
? 因為靜態(tài)數(shù)據(jù)成員在全局數(shù)據(jù)區(qū)分配內(nèi)存,屬于本類的所有對象共享陨闹,所以楞捂,它不屬于特定的類對象薄坏,在沒有產(chǎn)生類對象時其作用域就可見,即在沒有產(chǎn)生類的實例時寨闹,我們就可以操;
? 靜態(tài)數(shù)據(jù)成員主要用在各個對象都有相同的某項屬性的時候
同全局變量相比胶坠,使用靜態(tài)數(shù)據(jù)成員有兩個優(yōu)勢:

  1. 靜態(tài)數(shù)據(jù)成員沒有進入程序的全局名字空間,因此不存在與程序中其它全局名字沖突的可能性繁堡;
  2. 可以實現(xiàn)信息隱藏沈善。靜態(tài)數(shù)據(jù)成員可以是private成員,而全局變量不能

2.2靜態(tài)成員函數(shù)

一個靜態(tài)成員函數(shù)椭蹄,它為類的全部服務(wù)而不是為某一個類的具體對象服務(wù),靜態(tài)成員函數(shù)由于不是與任何的對象相聯(lián)系闻牡,因此它不具有this指針
關(guān)于靜態(tài)成員函數(shù),可以總結(jié)為以下幾點:
? 出現(xiàn)在類體外的函數(shù)定義不能指定關(guān)鍵字static绳矩;
? 靜態(tài)成員之間可以相互訪問罩润,包括靜態(tài)成員函數(shù)訪問靜態(tài)數(shù)據(jù)成員和訪問靜態(tài)成員函數(shù);
? 非靜態(tài)成員函數(shù)可以任意地訪問靜態(tài)成員函數(shù)和靜態(tài)數(shù)據(jù)成員翼馆;
? 靜態(tài)成員函數(shù)不能訪問非靜態(tài)成員函數(shù)和非靜態(tài)數(shù)據(jù)成員割以;
? 由于沒有this指針的額外開銷,因此靜態(tài)成員函數(shù)與類的全局函數(shù)相比速度上會有少許的增長应媚;


在C++程序中調(diào)用被C 語言修飾的函數(shù)严沥,為什么要加extern “C”?

extern "C"指令非常有用珍特,因為C和C++的近親關(guān)系祝峻。注意:extern "C"指令中的C,表示的一種編譯和連接規(guī)約扎筒,而不是一種語言莱找。C表示符合C語言的編譯和連接規(guī)約的任何語言,如Fortran嗜桌、assembler等奥溺。

extern "C"指令僅指定編譯和連接規(guī)約,但不影響語義骨宠。例如在函數(shù)聲明中浮定,指定了extern "C",仍然要遵守C++的類型檢測层亿、參數(shù)轉(zhuǎn)換規(guī)則

extern "C"的真實目的是實現(xiàn)類C和C++的混合編程桦卒。在C++源文件中的語句前面加上extern "C",表明它按照類C的編譯和連接規(guī)約來編譯和連接匿又,而不是C++的編譯的連接規(guī)約方灾。這樣在類C的代碼中就可以調(diào)用C++的函數(shù)or變量等

C++是一個面向?qū)ο笳Z言(雖不是純粹的面向?qū)ο笳Z言),它支持函數(shù)的重載,重載這個特性給我們帶來了很大的便利裕偿。為了支持函數(shù)重載的這個特性洞慎,C++編譯器實際上將下面這些重載函數(shù):

void print(int i);
void print(char c);
void print(float f);
void print(char* s);

編譯為:

_print_int
_print_char
_print_float
_pirnt_string

這樣的函數(shù)名,來唯一標識每個函數(shù)嘿棘。C++中的變量劲腿,編譯也類似,如全局變量可能編譯g_xx鸟妙,類變量編譯為c_xx等焦人。連接是也是按照這種機制去查找相應(yīng)的變量。
C語言中并沒有重載和類這些特性圆仔,故并不像C++那樣print(int i)垃瞧,會被編譯為_print_int,而是直接編譯為_print等坪郭。因此如果直接在C++中調(diào)用C的函數(shù)會失敗,因為連接是調(diào)用C中的print(3)時脉幢,它會去找_print_int(3)歪沃。因此extern "C"的作用就體現(xiàn)出來了

當我們C和C++混合編程時,有時候會用一種語言定義函數(shù)指針嫌松,而在應(yīng)用中將函數(shù)指針指向另一中語言定義的函數(shù)沪曙。如果C和C++共享同一中編譯和連接、函數(shù)調(diào)用機制萎羔,這樣做是可以的液走。然而,這樣的通用機制贾陷,通常不然假定它存在缘眶,因此我們必須小心地確保函數(shù)以期望的方式調(diào)用。

而且當指定一個函數(shù)指針的編譯和連接方式時髓废,函數(shù)的所有類型巷懈,包括函數(shù)名、函數(shù)引入的變量也按照指定的方式編譯和連接慌洪。如下例:

typedef int (*FT) (const void* ,const void*);//style of C++
 
extern "C"{
    typedef int (*CFT) (const void*,const void*);//style of C
    void qsort(void* p,size_t n,size_t sz,CFT cmp);//style of C
}
 
void isort(void* p,size_t n,size_t sz,FT cmp);//style of C++
void xsort(void* p,size_t n,size_t sz,CFT cmp);//style of C
 
//style of C
extern "C" void ysort(void* p,size_t n,size_t sz,FT cmp);
 
int compare(const void*,const void*);//style of C++
extern "C" ccomp(const void*,const void*);//style of C
 
void f(char* v,int sz)
{
    //error,as qsort is style of C
    //but compare is style of C++
    qsort(v,sz,1,&compare);
    qsort(v,sz,1,&ccomp);//ok
     
    isort(v,sz,1,&compare);//ok
    //error,as isort is style of C++
    //but ccomp is style of C
    isort(v,sz,1,&ccopm);
}

10 什么是內(nèi)存泄漏顶燕?什么是野指針?什么是內(nèi)存越界冈爹?如何避免涌攻?

10.1內(nèi)存泄漏

概念:用動態(tài)內(nèi)存分配函數(shù)動態(tài)開辟的空間,在使用完畢后未釋放频伤,程序結(jié)束后恳谎,會導致一直占據(jù)該內(nèi)存單元,直到程序結(jié)束剂买,在現(xiàn)代操作系統(tǒng)中惠爽,一個應(yīng)用程序使用的常規(guī)內(nèi)存在程序終止時被釋放癌蓖。這表示一個短暫運行的應(yīng)用程序中的內(nèi)存泄漏不會導致嚴重后果。但是在內(nèi)存非常有限的系統(tǒng)中都可能導致非常嚴重的后果婚肆,shared_ptr來避免內(nèi)存泄漏租副,但是要正確使用

10.2野指針

“野指針”不是NULL指針较性,是指指向“垃圾”內(nèi)存的指針用僧。即指針指向的內(nèi)容是不確定的。
產(chǎn)生的原因:
1)指針變量沒有初始化赞咙。因此责循,創(chuàng)建指針變量時,該變量要被置為NULL或者指向合法的內(nèi)存單元攀操。
2)指針p被free之后院仿,沒有置為NULL,讓人誤以為p是個合法的指針速和。
3)指針跨越合法范圍操作歹垫。不要返回指向棧內(nèi)存(非靜態(tài)局部變量)的指針或引用。
可能后果:

  • 若操作系統(tǒng)將這部分已經(jīng)釋放的內(nèi)存重新分配給另外一個進程颠放,而原來的程序重新引用現(xiàn)在的迷途指針排惨,向其中寫入數(shù)據(jù),則這部分程序內(nèi)容將被破壞碰凶,而導致程序錯誤暮芭。這種類型的程序錯誤,通常會導致segment fault和一般的保護錯誤欲低。
  • 其他常見錯誤:返回一個基于棧分配的局部變量的地址時辕宏,一旦調(diào)用的函數(shù)返回,分配給這些變量的空間將回收伸头,此時它們擁有的是垃圾值匾效,如return &num,如果要使它的生命周期邊長恤磷,應(yīng)該將其聲明為static

10.3 內(nèi)存越界

存在一種情況就是調(diào)用棧溢出(stackoverflow)面哼,還有一種情況是緩沖區(qū)溢出,這兩種情況都會導致安全漏洞扫步。

10.3.1緩沖區(qū)溢出

strcpy會一直復制直到碰到\0魔策,很多平臺的棧變量是按照地址順序倒著分配的(高地址向低地址),所以destination溢出后會先修改先前定義的變量河胎,這樣黑客就可以把is_administrator改為true闯袒,從而造成緩沖區(qū)溢出攻擊,當然數(shù)組越界也可以造成類似的效果,不過現(xiàn)在C++都提供了越界檢查的版本

// 緩沖區(qū)溢出攻擊
const int MAX_LENGTH = 16;
bool is_administrator = false;
char destination[MAX_LENGTH];
std::string source = read_string_from_client(); //內(nèi)容存儲在緩沖區(qū)
strcpy(destination,source.c_str());

10.3.2棧溢出攻擊

棧溢出攻擊:在棧上分配length字節(jié)的空間,再往棧頂放上一個data政敢。當Length十分大其徙,會把data擠到棧空間之外喷户,此時如果編譯器不做越界檢查的話唾那,那么黑客只要用客戶端送特定的length和data,就能改寫服務(wù)器的任意內(nèi)存(比如黑客可以修改服務(wù)器代碼的機器碼褪尝,注入一些JMP指令跳轉(zhuǎn)到黑客想執(zhí)行的函數(shù))

// 棧溢出攻擊
int length = read_int_from_client();
char buffer[length];    //椖只瘢空間分配
int data = read_int_from_client();

11 堆棧緩存的區(qū)別

1、棧使用的是一級緩存河哑, 他們通常都是被調(diào)用時處于存儲空間中避诽,調(diào)用完畢立即釋放;
2璃谨、堆是存放在二級緩存中沙庐,堆的首地址放在一級緩存緩存中,分配和釋放會產(chǎn)生系統(tǒng)調(diào)用睬罗,由用戶態(tài)進入內(nèi)核態(tài)轨功,所以速度會慢一些


12 STL 容器有哪些,常用的算法


13 如何理解智能指針,什么時候改變引用計數(shù)


14 share_ptr 與weak_ptr 的區(qū)別與聯(lián)系


15 C++構(gòu)造函數(shù)是否可以拋出異常

構(gòu)造函數(shù)可以拋出異常容达。但從邏輯上和風險控制上,構(gòu)造函數(shù)中盡量不要拋出異常垂券,既需要分配內(nèi)存花盐,又需要拋出異常時要特別注意防止內(nèi)存泄露的情況發(fā)生。因為在構(gòu)造函數(shù)中拋出異常菇爪,在概念上將被視為該對象沒有被成功構(gòu)造算芯,因此當前對象的析構(gòu)函數(shù)就不會被調(diào)用,就會造成內(nèi)存泄漏凳宙。同時熙揍,由于構(gòu)造函數(shù)本身也是一個函數(shù),在函數(shù)體內(nèi)拋出異常將導致當前函數(shù)運行結(jié)束氏涩,并釋放已經(jīng)構(gòu)造的成員對象届囚,包括其基類的成員,即執(zhí)行直接基類和成員對象的析構(gòu)函數(shù)


16 是否在析構(gòu)函數(shù)拋出異常

1)如果析構(gòu)函數(shù)拋出異常是尖,則異常點之后的程序不會執(zhí)行意系,如果析構(gòu)函數(shù)在異常點之后執(zhí)行了某些必要的動作比如釋放某些資源,則這些動作不會執(zhí)行饺汹,會造成諸如資源泄漏的問題蛔添。
2)通常異常發(fā)生時,c++的機制會調(diào)用已經(jīng)構(gòu)造對象的析構(gòu)函數(shù)來釋放資源,此時若析構(gòu)函數(shù)本身也拋出異常迎瞧,則前一個異常尚未處理夸溶,又有新的異常,會造成程序崩潰的問題凶硅。

  1. 那么當無法保證在析構(gòu)函數(shù)中不發(fā)生異常時缝裁, 其實還是有很好辦法來解決的。那就是把異常完全封裝在析構(gòu)函數(shù)內(nèi)部咏尝,決不讓異常拋出函數(shù)之外压语。這是一種非常簡單,也非常有效的方法编检。

17 volatile 的作用


18 構(gòu)造函數(shù)和析構(gòu)函數(shù)可以調(diào)用虛函數(shù)嗎

雖然可以對虛函數(shù)進行實調(diào)用胎食,但程序員編寫虛函數(shù)的本意應(yīng)該是實現(xiàn)動態(tài)聯(lián)編。在構(gòu)造函數(shù)中調(diào)用虛函數(shù)允懂,函數(shù)的入口地址是在編譯時靜態(tài)確定的厕怜,并未實現(xiàn)虛調(diào)用。但是為什么在構(gòu)造函數(shù)中調(diào)用虛函數(shù)蕾总,實際上沒有發(fā)生動態(tài)聯(lián)編呢粥航?
第一個原因,在概念上生百,構(gòu)造函數(shù)的工作是為對象進行初始化递雀。在構(gòu)造函數(shù)完成之前,被構(gòu)造的對象被認為“未完全生成”蚀浆。當創(chuàng)建某個派生類的對象時缀程,如果在它的基類的構(gòu)造函數(shù)中調(diào)用虛函數(shù),那么此時派生類的構(gòu)造函數(shù)并未執(zhí)行市俊,所調(diào)用的函數(shù)可能操作還沒有被初始化的成員杨凑,將導致災難的發(fā)生。
第二個原因摆昧,即使想在構(gòu)造函數(shù)中實現(xiàn)動態(tài)聯(lián)編撩满,在實現(xiàn)上也會遇到困難。這涉及到對象虛指針(vptr)的建立問題绅你。在Visual C++中伺帘,包含虛函數(shù)的類對象的虛指針被安排在對象的起始地址處,并且虛函數(shù)表(vtable)的地址是由構(gòu)造函數(shù)寫入虛指針的勇吊。所以曼追,一個類的構(gòu)造函數(shù)在執(zhí)行時,并不能保證該函數(shù)所能訪問到的虛指針就是當前被構(gòu)造對象最后所擁有的虛指針汉规,因為后面派生類的構(gòu)造函數(shù)會對當前被構(gòu)造對象的虛指針進行重寫礼殊,因此無法完成動態(tài)聯(lián)編

19 內(nèi)存對齊的原則

1).數(shù)據(jù)成員對齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員驹吮,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小或者成員的子成員大芯住(只要該成員有子成員碟狞,比如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int在32位機為4字節(jié), 則要從4的整數(shù)倍地址開始存儲),基本類型不包括struct/class/uinon婚陪。
2).結(jié)構(gòu)體作為成員:如果一個結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部"最寬基本類型成員"的整數(shù)倍地址開始存儲.(struct a里存有struct b,b里有char,int ,double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲.)族沃。
3).收尾工作:結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,.必須是其內(nèi)部最大成員的"最寬基本類型成員"的整數(shù)倍.不足的要補齊.(基本類型不包括struct/class/uinon)。
4).sizeof(union)泌参,以結(jié)構(gòu)里面size最大元素為union的size,因為在某一時刻脆淹,union只有一個成員真正存儲于該地

20 內(nèi)聯(lián)函數(shù)有什么優(yōu)點?內(nèi)聯(lián)函數(shù)和宏定義的區(qū)別沽一。

1.內(nèi)聯(lián)函數(shù)在運行時可調(diào)試盖溺,而宏定義不可以;
2.編譯器會對內(nèi)聯(lián)函數(shù)的參數(shù)類型做安全檢查或自動類型轉(zhuǎn)換(同普通函數(shù)),而宏定義則不會铣缠;
3.內(nèi)聯(lián)函數(shù)可以訪問類的成員變量烘嘱,宏定義則不能;
4.在類中聲明同時定義的成員函數(shù)蝗蛙,自動轉(zhuǎn)化為內(nèi)聯(lián)函數(shù)
內(nèi)聯(lián)函數(shù)和普通函數(shù)相比可以加快程序運行的速度蝇庭,因為不需要中斷調(diào)用,在編譯的時候內(nèi)聯(lián)函數(shù)可以直接被鑲嵌到目標代碼中捡硅。
內(nèi)聯(lián)函數(shù)要做參數(shù)類型檢查哮内,這是內(nèi)聯(lián)函數(shù)跟宏相比的優(yōu)勢。

inline一般只用于如下情況:
(1)一個函數(shù)不斷被重復調(diào)用壮韭。
(2)函數(shù)只有簡單的幾行牍蜂,且函數(shù)不包含for、while泰涂、switch語句,遞歸辐怕。

21 數(shù)組與指針的區(qū)別與聯(lián)系逼蒙,函數(shù)指針,指針函數(shù)寄疏,指針數(shù)組是牢,數(shù)組指針

22 STL set 和map 都是基于什么實現(xiàn)的

23 C++內(nèi)存泄露及檢測工具

檢測內(nèi)存泄漏的關(guān)鍵是要能截獲住對分配內(nèi)存和釋放內(nèi)存的函數(shù)的調(diào)用。截獲住這兩個函數(shù)陕截,我們就能跟蹤每一 塊內(nèi)存的生命周期驳棱,比如,每當成功的分配一塊內(nèi)存后农曲,就把它的指針加入一個全局的list中社搅;每當釋放一塊內(nèi)存驻债,再把它的指針從list中刪除。這樣形葬,當程序結(jié)束的時候合呐,list中剩余的指針就是指向那些沒有被釋放的內(nèi)存

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市笙以,隨后出現(xiàn)的幾起案子淌实,更是在濱河造成了極大的恐慌,老刑警劉巖猖腕,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拆祈,死亡現(xiàn)場離奇詭異,居然都是意外死亡倘感,警方通過查閱死者的電腦和手機放坏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侠仇,“玉大人轻姿,你說我怎么就攤上這事÷叽叮” “怎么了互亮?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長余素。 經(jīng)常有香客問我豹休,道長,這世上最難降的妖魔是什么桨吊? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任威根,我火速辦了婚禮,結(jié)果婚禮上视乐,老公的妹妹穿的比我還像新娘洛搀。我一直安慰自己,他們只是感情好佑淀,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布留美。 她就那樣靜靜地躺著,像睡著了一般伸刃。 火紅的嫁衣襯著肌膚如雪谎砾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天捧颅,我揣著相機與錄音景图,去河邊找鬼。 笑死碉哑,一個胖子當著我的面吹牛挚币,可吹牛的內(nèi)容都是我干的亮蒋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼忘晤,長吁一口氣:“原來是場噩夢啊……” “哼宛蚓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起设塔,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤凄吏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后闰蛔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痕钢,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年序六,在試婚紗的時候發(fā)現(xiàn)自己被綠了任连。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡例诀,死狀恐怖随抠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情繁涂,我是刑警寧澤拱她,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站扔罪,受9級特大地震影響秉沼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矿酵,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一唬复、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧全肮,春花似錦敞咧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至哪自,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間禁熏,已是汗流浹背壤巷。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瞧毙,地道東北人胧华。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓寄症,卻偏偏與公主長得像,于是被迫代替她去往敵國和親矩动。 傳聞我的和親對象是個殘疾皇子有巧,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

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