說一下static關鍵字的作用
參考回答:
在全局變量前加上關鍵字static,全局變量就定義成一個全局靜態變量.
靜態存儲區,在整個程序運行期間一直存在。
初始化:未經初始化的全局靜態變量會被自動初始化為0(自動對象的值是任意的,除非他被顯式初始化);
作用域:全局靜態變量在聲明他的文件之外是不可見的,准確地說是從定義之處開始,到文件結尾。
2. 局部靜態變量
在局部變量之前加上關鍵字static,局部變量就成為一個局部靜態變量。
內存中的位置:靜態存儲區
初始化:未經初始化的全局靜態變量會被自動初始化為0(自動對象的值是任意的,除非他被顯式初始化);
作用域:作用域仍為局部作用域,當定義它的函數或者語句塊結束的時候,作用域結束。但是當局部靜態變量離開作用域后,並沒有銷毀,而是仍然駐留在內存當中,只不過我們不能再對它進行訪問,直到該函數再次被調用,並且值不變;
3. 靜態函數
在函數返回類型前加static,函數就定義為靜態函數。函數的定義和聲明在默認情況下都是extern的,但靜態函數只是在聲明他的文件當中可見,不能被其他文件所用。
函數的實現使用static修飾,那么這個函數只可在本cpp內使用,不會同其他cpp中的同名函數引起沖突;
warning:不要再頭文件中聲明static的全局函數,不要在cpp內聲明非static的全局函數,如果你要在多個cpp中復用該函數,就把它的聲明提到頭文件里去,否則cpp內部聲明需加上static修飾;
4. 類的靜態成員
在類中,靜態成員可以實現多個對象之間的數據共享,並且使用靜態數據成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態成員是類的所有對象中共享的成員,而不是某個對象的成員。對多個對象來說,靜態數據成員只存儲一處,供所有對象共用
5. 類的靜態函數
靜態成員函數和靜態數據成員一樣,它們都屬於類的靜態成員,它們都不是對象成員。因此,對靜態成員的引用不需要用對象名。
● 說一下C++和C的區別
參考回答:
設計思想上:
C++是面向對象的語言,而C是面向過程的結構化編程語言
語法上:
C++具有封裝、繼承和多態三種特性
C++相比C,增加多許多類型安全的功能,比如強制類型轉換、
C++支持范式編程,比如模板類、函數模板等
● 說一說c++中四種cast轉換
參考回答:
C++中四種類型轉換是:static_cast, dynamic_cast, const_cast, reinterpret_cast
1、const_cast
用於將const變量轉為非const
2、static_cast
用於各種隱式轉換,比如非const轉const,void*轉指針等, static_cast能用於多態向上轉化,如果向下轉能成功但是不安全,結果未知;
3、dynamic_cast
用於動態類型轉換。只能用於含有虛函數的類,用於類層次間的向上和向下轉化。只能轉指針或引用。向下轉化時,如果是非法的對於指針返回NULL,對於引用拋異常。要深入了解內部轉換的原理。
向上轉換:指的是子類向基類的轉換
向下轉換:指的是基類向子類的轉換
它通過判斷在執行到該語句的時候變量的運行時類型和要轉換的類型是否相同來判斷是否能夠進行向下轉換。
4、reinterpret_cast
幾乎什么都可以轉,比如將int轉指針,可能會出問題,盡量少用;
5、為什么不使用C的強制轉換?
C的強制轉換表面上看起來功能強大什么都能轉,但是轉化不夠明確,不能進行錯誤檢查,容易出錯。
● 請說一下C/C++ 中指針和引用的區別?
參考回答:
1.指針有自己的一塊空間,而引用只是一個別名;
2.使用sizeof看一個指針的大小是4,而引用則是被引用對象的大小;
3.指針可以被初始化為NULL,而引用必須被初始化且必須是一個已有對象 的引用;
4.作為參數傳遞時,指針需要被解引用才可以對對象進行操作,而直接對引 用的修改都會改變引用所指向的對象;
5.可以有const指針,但是沒有const引用;
6.指針在使用中可以指向其它對象,但是引用只能是一個對象的引用,不能 被改變;
7.指針可以有多級指針(**p),而引用至於一級;
8.指針和引用使用++運算符的意義不一樣;
9.如果返回動態內存分配的對象或者內存,必須使用指針,引用可能引起內存泄露。
● 請你回答一下野指針是什么?
參考回答:
野指針就是指向一個已刪除的對象或者未申請訪問受限內存區域的指針
● 請你回答一下智能指針有沒有內存泄露的情況
參考回答:
當兩個對象相互使用一個shared_ptr成員變量指向對方,會造成循環引用,使引用計數失效,從而導致內存泄漏。例如:


● 請你來說一下智能指針的內存泄漏如何解決
參考回答:
為了解決循環引用導致的內存泄漏,引入了weak_ptr弱指針,weak_ptr的構造函數不會修改引用計數的值,從而不會對對象的內存進行管理,其類似一個普通指針,但不指向引用計數的共享內存,但是其可以檢測到所管理的對象是否已經被釋放,從而避免非法訪問。
● 請你回答一下為什么析構函數必須是虛函數?為什么C++默認的析構函數不是虛函數 考點:虛函數 析構函數
參考回答:
將可能會被繼承的父類的析構函數設置為虛函數,可以保證當我們new一個子類,然后使用基類指針指向該子類對象,釋放基類指針時可以釋放掉子類的空間,防止內存泄漏。
C++默認的析構函數不是虛函數是因為虛函數需要額外的虛函數表和虛表指針,占用額外的內存。而對於不會被繼承的類來說,其析構函數如果是虛函數,就會浪費內存。因此C++默認的析構函數不是虛函數,而是只有當需要當作父類時,設置為虛函數。
● 請你來說一下函數指針
參考回答:
1、定義
函數指針是指向函數的指針變量。
函數指針本身首先是一個指針變量,該指針變量指向一個具體的函數。這正如用指針變量可指向整型變量、字符型、數組一樣,這里是指向函數。
C在編譯時,每一個函數都有一個入口地址,該入口地址就是函數指針所指向的地址。有了指向函數的指針變量后,可用該指針變量調用函數,就如同用指針變量可引用其他類型變量一樣,在這些概念上是大體一致的。
2、用途:
調用函數和做函數的參數,比如回調函數。
3、示例:
char * fun(char * p) {…} // 函數fun
char * (*pf)(char * p); // 函數指針pf
pf = fun; // 函數指針pf指向函數fun
pf(p); // 通過函數指針pf調用函數fun
● 請你來說一下fork函數
參考回答:
Fork:創建一個和當前進程映像一樣的進程可以通過fork( )系統調用:
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
成功調用fork( )會創建一個新的進程,它幾乎與調用fork( )的進程一模一樣,這兩個進程都會繼續運行。在子進程中,成功的fork( )調用會返回0。在父進程中fork( )返回子進程的pid。如果出現錯誤,fork( )返回一個負值。
最常見的fork( )用法是創建一個新的進程,然后使用exec( )載入二進制映像,替換當前進程的映像。這種情況下,派生(fork)了新的進程,而這個子進程會執行一個新的二進制可執行文件的映像。這種“派生加執行”的方式是很常見的。
在早期的Unix系統中,創建進程比較原始。當調用fork時,內核會把所有的內部數據結構復制一份,復制進程的頁表項,然后把父進程的地址空間中的內容逐頁的復制到子進程的地址空間中。但從內核角度來說,逐頁的復制方式是十分耗時的。現代的Unix系統采取了更多的優化,例如Linux,采用了寫時復制的方法,而不是對父進程空間進程整體復制。
● 請你來說一下C++中析構函數的作用
參考回答:
析構函數與構造函數對應,當對象結束其生命周期,如對象所在的函數已調用完畢時,系統會自動執行析構函數。
析構函數名也應與類名相同,只是在函數名前面加一個位取反符~,例如~stud( ),以區別於構造函數。它不能帶任何參數,也沒有返回值(包括void類型)。只能有一個析構函數,不能重載。
如果用戶沒有編寫析構函數,編譯系統會自動生成一個缺省的析構函數(即使自定義了析構函數,編譯器也總是會為我們合成一個析構函數,並且如果自定義了析構函數,編譯器在執行時會先調用自定義的析構函數再調用合成的析構函數),它也不進行任何操作。所以許多簡單的類中沒有用顯式的析構函數。
如果一個類中有指針,且在使用的過程中動態的申請了內存,那么最好顯示構造析構函數在銷毀類之前,釋放掉申請的內存空間,避免內存泄漏。
類析構順序:1)派生類本身的析構函數;2)對象成員析構函數;3)基類析構函數。
● 請你來說一下靜態函數和虛函數的區別
參考回答:
靜態函數在編譯的時候就已經確定運行時機,虛函數在運行的時候動態綁定。虛函數因為用了虛函數表機制,調用的時候會增加一次內存開銷
● 請你來說一說重載和覆蓋
參考回答:
重載:兩個函數名相同,但是參數列表不同(個數,類型),返回值類型沒有要求,在同一作用域中
重寫:子類繼承了父類,父類中的函數是虛函數,在子類中重新定義了這個虛函數,這種情況是重寫
● 請你說一說strcpy和strlen
參考回答:
strcpy是字符串拷貝函數,原型:
char *strcpy(char* dest, const char *src);
從src逐字節拷貝到dest,直到遇到'\0'結束,因為沒有指定長度,可能會導致拷貝越界,造成緩沖區溢出漏洞,安全版本是strncpy函數。
strlen函數是計算字符串長度的函數,返回從開始到'\0'之間的字符個數。
● 請你說一說你理解的虛函數和多態
參考回答:
多態的實現主要分為靜態多態和動態多態,靜態多態主要是重載,在編譯的時候就已經確定;動態多態是用虛函數機制實現的,在運行期間動態綁定。舉個例子:一個父類類型的指針指向一個子類對象時候,使用父類的指針去調用子類中重寫了的父類中的虛函數的時候,會調用子類重寫過后的函數,在父類中聲明為加了virtual關鍵字的函數,在子類中重寫時候不需要加virtual也是虛函數。
虛函數的實現:在有虛函數的類中,類的最開始部分是一個虛函數表的指針,這個指針指向一個虛函數表,表中放了虛函數的地址,實際的虛函數在代碼段(.text)中。當子類繼承了父類的時候也會繼承其虛函數表,當子類重寫父類中虛函數時候,會將其繼承到的虛函數表中的地址替換為重新寫的函數地址。使用了虛函數,會增加訪問內存開銷,降低效率。
● 請你來說一下C++里是怎么定義常量的?常量存放在內存的哪個位置?
參考回答:
常量在C++里的定義就是一個top-level const加上對象類型,常量定義必須初始化。對於局部對象,常量存放在棧區,對於全局對象,常量存放在全局/靜態存儲區。對於字面值常量,常量存放在常量存儲區。
● 請你來回答一下const修飾成員函數的目的是什么?
參考回答:
const修飾的成員函數表明函數調用不會對對象做出任何更改,事實上,如果確認不會對對象做更改,就應該為函數加上const限定,這樣無論const對象還是普通對象都可以調用該函數。
● 如果同時定義了兩個函數,一個帶const,一個不帶,會有問題嗎?
參考回答:
不會,這相當於函數的重載。
● 請你來說一說隱式類型轉換
參考回答:
首先,對於內置類型,低精度的變量給高精度變量賦值會發生隱式類型轉換,其次,對於只存在單個參數的構造函數的對象構造來說,函數調用可以直接使用該參數傳入,編譯器會自動調用其構造函數生成臨時對象。
● 說說你了解的類型轉換
參考回答:
reinterpret_cast:可以用於任意類型的指針之間的轉換,對轉換的結果不做任何保證
dynamic_cast:這種其實也是不被推薦使用的,更多使用static_cast,dynamic本身只能用於存在虛函數的父子關系的強制類型轉換,對於指針,轉換失敗則返回nullptr,對於引用,轉換失敗會拋出異常
const_cast:對於未定義const版本的成員函數,我們通常需要使用const_cast來去除const引用對象的const,完成函數調用。另外一種使用方式,結合static_cast,可以在非const版本的成員函數內添加const,調用完const版本的成員函數后,再使用const_cast去除const限定。
static_cast:完成基礎數據類型;同一個繼承體系中類型的轉換;任意類型與空指針類型void* 之間的轉換。
● 請你回答一下new/delete與malloc/free的區別是什么
參考回答:
首先,new/delete是C++的關鍵字,而malloc/free是C語言的庫函數,后者使用必須指明申請內存空間的大小,對於類類型的對象,后者不會調用構造函數和析構函數
● 請你說說虛函數表具體是怎樣實現運行時多態的?
參考回答:
子類若重寫父類虛函數,虛函數表中,該函數的地址會被替換,對於存在虛函數的類的對象,在VS中,對象的對象模型的頭部存放指向虛函數表的指針,通過該機制實現多態。
● 請你說說C語言是怎么進行函數調用的?
參考回答:
每一個函數調用都會分配函數棧,在棧內進行函數執行過程。調用前,先把返回地址壓棧,然后把當前函數的esp指針壓棧。
● 請你說說C語言參數壓棧順序?
參考回答:
從右到左
● 請你說說C++如何處理返回值?
參考回答:
生成一個臨時變量,把它的引用作為函數參數傳入函數內。
● 請你回答一下C++中拷貝賦值函數的形參能否進行值傳遞?
參考回答:
不能。如果是這種情況下,調用拷貝構造函數的時候,首先要將實參傳遞給形參,這個傳遞的時候又要調用拷貝構造函數。。如此循環,無法完成拷貝,棧也會滿。