1.請說出static和const關鍵字盡可能多的作用
static關鍵字至少有下列n個作用:
(1)函數體內static變量的作用范圍為該函數體,不同于auto變量,該變量的內存只被分配一次,因此其值在下次調用時仍維持上次的值;
(2)在模塊內的static全局變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問;
(3)在模塊內的static函數只可被這一模塊內的其它函數調用,這個函數的使用范圍被限制在聲明它的模塊內;
(4)在類中的static成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝;
(5)在類中的static成員函數屬于整個類所擁有,這個函數不接收this指針,因而只能訪問類的static成員變量。
(6)static修飾的變量在main函數運行前便分配空間
const關鍵字至少有下列n個作用:
(1)欲阻止一個變量被改變,可以使用const關鍵字。在定義該const變量時,通常需要對它進行初始化,因為以后就沒有機會再去改變它了;
(2)對指針來說,可以指定指針本身為const,也可以指定指針所指的數據為const,或二者同時指定為const;
(3)在一個函數聲明中,const可以修飾形參,表明它是一個輸入參數,在函數內部不能改變其值;
(4)對于類的成員函數,若指定其為const類型,則表明其是一個常函數,不能修改類的 成員變量;
(5)對于類的成員函數,有時候必須指定其返回值為const類型,以使得其返回值不為“左值”。例如:
const classA operator(const classA& a1,const classA& a2);
operator的返回結果必須是一個const對象。如果不是,這樣的變態(tài)代碼也不會編譯出錯:
classA a, b, c;
(a * b) = c; // 對a*b的結果賦值
操作(a * b) = c顯然不符合編程者的初衷,也沒有任何意義。
2.說一下static關鍵字的作用
全局靜態(tài)變量
在全局變量前加上關鍵字static,全局變量就定義成一個全局靜態(tài)變量.
靜態(tài)存儲區(qū),在整個程序運行期間一直存在。
初始化:未經初始化的全局靜態(tài)變量會被自動初始化為0(自動對象的值是任意的,除非他被顯式初始化);
作用域:全局靜態(tài)變量在聲明他的文件之外是不可見的,準確地說是從定義之處開始,到文件結尾。局部靜態(tài)變量
在局部變量之前加上關鍵字static,局部變量就成為一個局部靜態(tài)變量。
內存中的位置:靜態(tài)存儲區(qū)
初始化:未經初始化的全局靜態(tài)變量會被自動初始化為0(自動對象的值是任意的,除非他被顯式初始化);
作用域:作用域仍為局部作用域,當定義它的函數或者語句塊結束的時候,作用域結束。但是當局部靜態(tài)變量離開作用域后,并沒有銷毀,而是仍然駐留在內存當中,只不過我們不能再對它進行訪問,直到該函數再次被調用,并且值不變;靜態(tài)函數
在函數返回類型前加static,函數就定義為靜態(tài)函數。函數的定義和聲明在默認情況下都是extern的,但靜態(tài)函數只是在聲明他的文件當中可見,不能被其他文件所用。
函數的實現使用static修飾,那么這個函數只可在本cpp內使用,不會同其他cpp中的同名函數引起沖突;
warning:不要再頭文件中聲明static的全局函數,不要在cpp內聲明非static的全局函數,如果你要在多個cpp中復用該函數,就把它的聲明提到頭文件里去,否則cpp內部聲明需加上static修飾;類的靜態(tài)成員
在類中,靜態(tài)成員可以實現多個對象之間的數據共享,并且使用靜態(tài)數據成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態(tài)成員是類的所有對象中共享的成員,而不是某個對象的成員。對多個對象來說,靜態(tài)數據成員只存儲一處,供所有對象共用類的靜態(tài)函數
靜態(tài)成員函數和靜態(tài)數據成員一樣,它們都屬于類的靜態(tài)成員,它們都不是對象成員。因此,對靜態(tài)成員的引用不需要用對象名。
在靜態(tài)成員函數的實現中不能直接引用類中說明的非靜態(tài)成員,可以引用類中說明的靜態(tài)成員(這點非常重要)。如果靜態(tài)成員函數中要引用非靜態(tài)成員時,可通過對象來引用。從中可看出,調用靜態(tài)成員函數使用如下格式:<類名>::<靜態(tài)成員函數名>(<參數表>);
3.C和C++區(qū)別
設計思想上:
C++是面向對象的語言,而C是面向過程的結構化編程語言
語法上:
C++具有封裝、繼承和多態(tài)三種特性
C++相比C,增加多許多類型安全的功能,比如強制類型轉換
C++支持范式編程,比如模板類、函數模板等
4.指針和引用區(qū)別
1.指針有自己的一塊空間,而引用只是一個別名;
2.使用sizeof看一個指針的大小是4,而引用則是被引用對象的大小;
3.指針可以被初始化為NULL,而引用必須被初始化且必須是一個已有對象 的引用;
4.作為參數傳遞時,指針需要被解引用才可以對對象進行操作,而直接對引 用的修改都會改變引用所指向的對象;
5.可以有const指針,但是沒有const引用;
6.指針在使用中可以指向其它對象,但是引用只能是一個對象的引用,不能 被改變;
7.指針可以有多級指針(**p),而引用至于一級;
8.指針和引用使用++運算符的意義不一樣;
9.如果返回動態(tài)內存分配的對象或者內存,必須使用指針,引用可能引起內存泄露。
5.請你說一下你理解的c++中的smart pointer四個智能指針:shared_ptr,unique_ptr,weak_ptr,auto_ptr



6.為什么析構函數必須是虛函數?為什么C++默認的析構函數不是虛函數
將可能會被繼承的父類的析構函數設置為虛函數,可以保證當我們new一個子類,然后使用基類指針指向該子類對象,釋放基類指針時可以釋放掉子類的空間,防止內存泄漏。
C++默認的析構函數不是虛函數是因為虛函數需要額外的虛函數表和虛表指針,占用額外的內存。而對于不會被繼承的類來說,其析構函數如果是虛函數,就會浪費內存。因此C++默認的析構函數不是虛函數,而是只有當需要當作父類時,設置為虛函數。
7.請你來說一下fork函數
Fork:創(chuàng)建一個和當前進程映像一樣的進程可以通過fork( )系統(tǒng)調用:
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
成功調用fork( )會創(chuàng)建一個新的進程,它幾乎與調用fork( )的進程一模一樣,這兩個進程都會繼續(xù)運行。在子進程中,成功的fork( )調用會返回0。在父進程中fork( )返回子進程的pid。如果出現錯誤,fork( )返回一個負值。
最常見的fork( )用法是創(chuàng)建一個新的進程,然后使用exec( )載入二進制映像,替換當前進程的映像。這種情況下,派生(fork)了新的進程,而這個子進程會執(zhí)行一個新的二進制可執(zhí)行文件的映像。這種“派生加執(zhí)行”的方式是很常見的。
在早期的Unix系統(tǒng)中,創(chuàng)建進程比較原始。當調用fork時,內核會把所有的內部數據結構復制一份,復制進程的頁表項,然后把父進程的地址空間中的內容逐頁的復制到子進程的地址空間中。但從內核角度來說,逐頁的復制方式是十分耗時的?,F代的Unix系統(tǒng)采取了更多的優(yōu)化,例如Linux,采用了寫時復制的方法,而不是對父進程空間進程整體復制。
8.map和set有什么區(qū)別,分別又是怎么實現的?
map和set都是C++的關聯容器,其底層實現都是紅黑樹(RB-Tree)。由于 map 和set所開放的各種操作接口,RB-tree 也都提供了,所以幾乎所有的 map 和set的操作行為,都只是轉調 RB-tree 的操作行為。
map和set區(qū)別在于:
(1)map中的元素是key-value(關鍵字—值)對:關鍵字起到索引的作用,值則表示與索引相關聯的數據;Set與之相對就是關鍵字的簡單集合,set中每個元素只包含一個關鍵字。
(2)set的迭代器是const的,不允許修改元素的值;map允許修改value,但不允許修改key。其原因是因為map和set是根據關鍵字排序來保證其有序性的,如果允許修改key的話,那么首先需要刪除該鍵,然后調節(jié)平衡,再插入修改后的鍵值,調節(jié)平衡,如此一來,嚴重破壞了map和set的結構,導致iterator失效,不知道應該指向改變前的位置,還是指向改變后的位置。所以STL中將set的迭代器設置成const,不允許修改迭代器的值;而map的迭代器則不允許修改key值,允許修改value值。
(3)map支持下標操作,set不支持下標操作。map可以用key做下標,map的下標運算符[ ]將關鍵碼作為下標去執(zhí)行查找,如果關鍵碼不存在,則插入一個具有該關鍵碼和mapped_type類型默認值的元素至map中,因此下標運算符[ ]在map應用中需要慎用,const_map不能用,只希望確定某一個關鍵值是否存在而不希望插入元素時也不應該使用,mapped_type類型沒有默認值也不應該使用。如果find能解決需要,盡可能用find。
9.STL的allocator
STL的分配器用于封裝STL容器在內存管理上的底層細節(jié)。在C++中,其內存配置和釋放如下:
new運算分兩個階段:(1)調用::operator new配置內存;(2)調用對象構造函數構造對象內容
delete運算分兩個階段:(1)調用對象析構函數;(2)調用::operator delete釋放內存
為了精密分工,STL allocator將兩個階段操作區(qū)分開來:內存配置有alloc::allocate()負責,內存釋放由alloc::deallocate()負責;對象構造由::construct()負責,對象析構由::destroy()負責。
同時為了提升內存管理的效率,減少申請小內存造成的內存碎片問題,SGI STL采用了兩級配置器,當分配的空間大小超過128B時,會使用第一級空間配置器;當分配的空間大小小于128B時,將使用第二級空間配置器。第一級空間配置器直接使用malloc()、realloc()、free()函數進行內存空間的分配和釋放,而第二級空間配置器采用了內存池技術,通過空閑鏈表來管理內存。
10.C++源文件從文本到可執(zhí)行文件經歷的過程
對于C++源文件,從文本到可執(zhí)行文件一般需要四個過程:
預處理階段:對源代碼文件中文件包含關系(頭文件)、預編譯語句(宏定義)進行分析和替換,生成預編譯文件。
編譯階段:將經過預處理后的預編譯文件轉換成特定匯編代碼,生成匯編文件
匯編階段:將編譯階段生成的匯編文件轉化成機器碼,生成可重定位目標文件
鏈接階段:將多個目標文件及所需要的庫連接成最終的可執(zhí)行目標文件