面試知識點(1)C++語言基礎(chǔ)

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

1.指針有自己的一塊空間,而引用只是一個別名;
2.使用sizeof看一個指針的大小是4,而引用則是被引用對象的大??;
3.指針可以被初始化為NULL,而引用必須被初始化且必須是一個已有對象的引用;
4.作為參數(shù)傳遞時,指針需要被解引用才可以對對象進行操作,而直接對引用的修改都會改變引用所指向的對象;
5.可以有const指針,但是沒有const引用;
6.指針在使用中可以指向其它對象,但是引用只能是一個對象的引用,不能改變;
9.如果返回動態(tài)內(nèi)存分配的對象或者內(nèi)存,必須使用指針,引用可能引起內(nèi)存泄露

2. 堆和棧的區(qū)別

1)棧,由編譯器自動管理,無需我們手工控制;堆,申請釋放工作由程序員控制
2)堆的生長方向是向上的,也就是向著內(nèi)存地址增加的方向;棧的生長方向是向下的,是向著內(nèi)存地址減小的方向增長。
3)對于堆來講,頻繁的 new/delete 勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個問題
4)一般來講在 32 位系統(tǒng)下,堆內(nèi)存可以達到4G的空間,但是對于棧來講,一般都是有一定的空間大小的(2M)。

3. new與malloc區(qū)別

  1. new分配內(nèi)存按照數(shù)據(jù)類型進行分配,malloc分配內(nèi)存按照大小分配;
  2. new不僅分配一段內(nèi)存,而且會==調(diào)用構(gòu)造函數(shù)==,但是malloc則不會;delete銷毀的時候會調(diào)用對象的析構(gòu)函數(shù),而free則不會;
  3. new返回的是指定對象的指針,而malloc返回的是void*,因此malloc的返回值一般都需要進行類型轉(zhuǎn)化;
  4. new是一個==操作符==可以重載,malloc是一個==庫函數(shù)==;
  5. new如果分配失敗了會拋出bad_malloc的異常,而malloc失敗了會返回NULL。
  6. new和new[]的區(qū)別,new[]一次分配所有內(nèi)存,多次調(diào)用構(gòu)造函數(shù),分別搭配使用delete和delete[],同理,delete[]多次調(diào)用析構(gòu)函數(shù),銷毀數(shù)組中的每個對象。

4. Struct和class的區(qū)別

默認的繼承訪問權(quán)限struct是public的,class是private的。

5.define 和 const 的區(qū)別(編譯階段、安全性、內(nèi)存占用等)

define只是在預(yù)編譯時進行字符置換,把程序中出現(xiàn)的字符串PI全部換成3.14159。在預(yù)編譯之后,程序中不再有PI這個標(biāo)識符。PI不是變量,沒有類型,不占用存儲單元,而且容易出錯

6.在C++中const和static的用法(定義,用途)

● 請說一下static的作用

  1. 全局靜態(tài)變量
    內(nèi)存中的位置:靜態(tài)存儲區(qū),在整個程序運行期間一直存在。初始化:未經(jīng)初始化的全局靜態(tài)變量會被自動初始化為0(自動對象的值是任意的,除非他被顯式初始化)。
    作用域:全局靜態(tài)變量在聲明他的文件之外是不可見的,準確地說是從定義之處開始,到文件結(jié)尾。
  2. 局部靜態(tài)變量
    其余同全局靜態(tài)變量。
    作用域:作用域仍為局部作用域,當(dāng)定義它的函數(shù)或者語句塊結(jié)束的時候,作用域結(jié)束。但是當(dāng)局部靜態(tài)變量離開作用域后,并沒有銷毀,而是仍然駐留在內(nèi)存當(dāng)中,只不過我們不能再對它進行訪問,直到該函數(shù)再次被調(diào)用,并且值不變。
  3. 靜態(tài)函數(shù)
    數(shù)的定義和聲明在默認情況下都是extern的,但靜態(tài)函數(shù)只是在聲明他的文件當(dāng)中可見,不能被其他文件所用。函數(shù)只可在本cpp內(nèi)使用,不會同其他cpp中的同名函數(shù)引起沖突。
  4. 類的靜態(tài)成員
    靜態(tài)成員是類的所有對象中共享的成員,而不是某個對象的成員。對多個對象來說,靜態(tài)數(shù)據(jù)成員只存儲一處,供所有對象共用。
  5. 類的靜態(tài)函數(shù)
    靜態(tài)成員函數(shù)和靜態(tài)數(shù)據(jù)成員一樣,它們都屬于類的靜態(tài)成員,它們都不是對象成員。因此,對靜態(tài)成員的引用不需要用對象名。
    在靜態(tài)成員函數(shù)的實現(xiàn)中不能直接引用類中說明的非靜態(tài)成員,可以引用類中說明的靜態(tài)成員(這點非常重要)。如果靜態(tài)成員函數(shù)中要引用非靜態(tài)成員時,通過對象來引用。

● 說說const的作用,越多越好

  1. const修飾全局變量、const修飾局部變量為不可修改;
  2. const在*左側(cè),const就是用來修飾指針?biāo)赶虻淖兞?,即指針指向為常量const int * a;
  3. const在*右側(cè),const就是修飾指針本身,即指針本身是常量,指針本身不能改變,指針指向的內(nèi)容可以改變 int * const a;
  4. const修飾引用做形參,提高效率;
  5. const修飾成員變量,必須在構(gòu)造函數(shù)列表中初始化;
  6. const修飾成員函數(shù),const成員函數(shù)不能修改類中的成員變量,只讀不寫。當(dāng)const在函數(shù)名前面的時候修飾的是函數(shù)返回值

7.計算下面幾個類的大小:

class A {};: sizeof(A) = 1;
class A { virtual Fun(){} };: sizeof(A) = 4(32位機器)/8(64位機器);
class A { static int a; };: sizeof(A) = 1;
class A { int a; };: sizeof(A) = 4;
class A { static int a; int b; };: sizeof(A) = 4;
● 構(gòu)造與析構(gòu)順序徐

 static E e2;
 C c;
int main()
{
    A *a = new A();
    B b;

    static D d;
    delete a;
    return 0;
}
class E constructor
class C constructor
class A constructor
class B constructor
class D constructor

class A destructor
class B destructor
class D destructor
class C destructor
class E destructor

8.請你來說一說重載和重寫

重載:兩個函數(shù)名相同,但是參數(shù)列表不同(個數(shù),類型),返回值類型沒有要求,在同一作用域中
重寫:子類繼承了父類,父類中的函數(shù)是虛函數(shù),在子類中重新定義了這個虛函數(shù)。

9.C ++內(nèi)存管理(熱門問題)

在C++中,虛擬內(nèi)存分為代碼段、數(shù)據(jù)段、BSS段、堆區(qū)、文件映射區(qū)以及棧區(qū)六部分。
代碼段: 包括只讀存儲區(qū)和文本區(qū),其中只讀存儲區(qū)存儲==字符串常量==,文本區(qū)存儲==程序的機器代碼==。
數(shù)據(jù)段:存儲程序中==已初始化的全局變量和靜態(tài)變量==
bss 段:存儲==未初始化的全局變量和靜態(tài)變量(局部+全局)==,以及所有被初始化為0的全局變量和靜態(tài)變量。
映射區(qū):存儲==動態(tài)鏈接庫==以及調(diào)用mmap函數(shù)進行的文件映射
:使用??臻g存儲函數(shù)的返回地址、參數(shù)、局部變量、返回值
堆區(qū):調(diào)用new/malloc函數(shù)時在堆區(qū)動態(tài)分配內(nèi)存,同時需要調(diào)用delete/free來手動釋放申請的內(nèi)存。

10.結(jié)構(gòu)體內(nèi)存對齊方式和為什么要進行內(nèi)存對齊?

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

  1. 從0位置開始存儲;
  2. 變量存儲的起始位置是該變量大小的整數(shù)倍;
  3. 結(jié)構(gòu)體總的大小是其最大元素的整數(shù)倍,不足的后面要補齊;
  4. 結(jié)構(gòu)體中包含結(jié)構(gòu)體,從結(jié)構(gòu)體中最大元素的整數(shù)倍開始存;
  5. 如果加入pragma pack(n) ,取n和變量自身大小較小的一個。
    原因
    A 不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的;
    B 未對齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對齊的內(nèi)存訪問僅需要一次訪問,==加快速度==

11 .C++中內(nèi)存泄漏的幾種情況

  1. 在類的構(gòu)造函數(shù)和析構(gòu)函數(shù)中沒有匹配的調(diào)用new和delete函數(shù)

  2. 沒有正確地清除嵌套的對象指針

  3. 在釋放對象數(shù)組時在delete中沒有使用方括號

  4. 缺少拷貝構(gòu)造函數(shù)

  5. 沒有將基類的析構(gòu)函數(shù)定義為虛函數(shù)

野指針:指向被釋放的或者訪問受限內(nèi)存的指針。
造成野指針的原因:
指針變量沒有被初始化,指針被free或者delete后,沒有置為NULL, free和delete只是把指針?biāo)赶虻?strong>內(nèi)存給釋放掉,并沒有把指針本身干掉,此時指針指向的是“垃圾”內(nèi)存。釋放后的指針應(yīng)該被置為NULL.
指針操作超越了變量的作用范圍,比如返回指向棧內(nèi)存的指針就是野指針。

造成的后果
性能不良,內(nèi)存會耗盡

12.內(nèi)存泄漏如何排查

調(diào)試運行DEBUG版程序,運用以下技術(shù):CRT(C run-time libraries)、運行時函數(shù)調(diào)用堆棧、內(nèi)存泄漏時提示的內(nèi)存分配序號(集成開發(fā)環(huán)境OUTPUT窗口),綜合分析內(nèi)存泄漏的原因,排除內(nèi)存泄漏。
linux工具之檢測內(nèi)存泄漏-valgrind,功能強大,不僅僅是內(nèi)存泄漏檢測工具。

13.怎么有效解決內(nèi)存泄漏問題?

智能指針。因為智能指針可以自動刪除分配的內(nèi)存。智能指針和普通指針類似,只是不需要手動釋放指針,而是通過智能指針自己管理內(nèi)存的釋放

14. 多態(tài)的實現(xiàn)(和下個問題一起回答)

15. C++虛函數(shù)相關(guān)(虛函數(shù)表,虛函數(shù)指針),虛函數(shù)的實現(xiàn)原理(熱門,重要)

  1. C++多態(tài)的實現(xiàn)?
    多態(tài)分為靜態(tài)多態(tài)和動態(tài)多態(tài)。靜態(tài)多態(tài)是通過重載和模板技術(shù)實現(xiàn),在編譯的時候確定。動態(tài)多態(tài)通過虛函數(shù)和繼承關(guān)系來實現(xiàn),執(zhí)行動態(tài)綁定,在運行的時候確定.
  2. 動態(tài)多態(tài)的實現(xiàn):
    C++中會為每一個類內(nèi)部生成一個虛函數(shù)表,該表中存放著虛函數(shù)的地址,每個對象的地址的首部都有一個虛指針,指向虛函數(shù)表。通過對象執(zhí)行虛函數(shù)時,都會通過虛指針找到虛函數(shù)表,再從虛函數(shù)表中找到虛函數(shù)的地址進行調(diào)用。當(dāng)子類在改寫了父類中的虛函數(shù)時,子類的虛指針指向的虛函數(shù)表中的存放的虛函數(shù)指針不再是父類的虛函數(shù)地址了,而是子類所改寫父類的虛函數(shù)地址。
  3. 虛函數(shù)的作用?
    a) 虛函數(shù)用于實現(xiàn)多態(tài),這點大家都能答上來
    b) 但是虛函數(shù)在設(shè)計上還具有封裝和抽象的作用。比如抽象工廠模式。
  4. 子類析構(gòu)函數(shù)必須是虛函數(shù),構(gòu)造函數(shù)不能是虛函數(shù)。析構(gòu)和構(gòu)造函數(shù)調(diào)用虛函數(shù)與普通函數(shù)一樣
    構(gòu)造函數(shù)不能是虛函數(shù),每個對象的虛函數(shù)表指針是在構(gòu)造函數(shù)中初始化的,因為構(gòu)造函數(shù)沒執(zhí)行完,所以虛函數(shù)表指針還沒初始化好,構(gòu)造函數(shù)的虛函數(shù)不起作用,所以構(gòu)造函數(shù)不能是虛函數(shù)。如果有子類的話,析構(gòu)函數(shù)必須是虛函數(shù)。否則析構(gòu)子類類型的指針時,析構(gòu)函數(shù)有可能不會被調(diào)用到。

16.引用是否能實現(xiàn)動態(tài)綁定,為什么引用可以實現(xiàn)

因為對象的類型是確定的,在編譯期就確定了
指針或引用是在運行期根據(jù)他們綁定的具體對象確定。

17.深拷貝和淺拷貝的區(qū)別

淺拷貝只是對指針的拷貝,拷貝后兩個指針指向同一個內(nèi)存空間,深拷貝不但對指針進行拷貝,而且對指針指向的內(nèi)容進行拷貝,經(jīng)深拷貝后的指針是指向兩個不同地址的指針。

18.調(diào)用拷貝構(gòu)造函數(shù)的三種情況

1.當(dāng)用類的對象去初始化同類的另一個對象時。
2.當(dāng)函數(shù)的形參是類的對象,調(diào)用函數(shù)進行形參和實參結(jié)合時。
3.當(dāng)函數(shù)的返回值是對象,函數(shù)執(zhí)行完成返回調(diào)用者時。

19.說一說c++中四種cast轉(zhuǎn)換

C++中四種類型轉(zhuǎn)換是:static_cast, dynamic_cast, const_cast, reinterpret_cast
1、const_cast
用于將const變量轉(zhuǎn)為非const
2、static_cast
用于各種隱式轉(zhuǎn)換,比如非const轉(zhuǎn)const,void*轉(zhuǎn)指針等, static_cast能用于多態(tài)向上轉(zhuǎn)化,如果向下轉(zhuǎn)能成功但是不安全,結(jié)果未知;
3、dynamic_cast
用于動態(tài)類型轉(zhuǎn)換。只能用于含有虛函數(shù)的類,用于類層次間的向上和向下轉(zhuǎn)化。只能轉(zhuǎn)指針或引用。向下轉(zhuǎn)化時,如果是非法的對于指針返回NULL,對于引用拋異常。
向上轉(zhuǎn)換:指的是子類向基類的轉(zhuǎn)換 向下轉(zhuǎn)換:指的是基類向子類的轉(zhuǎn)換
它通過判斷在執(zhí)行到該語句的時候變量的運行時類型和要轉(zhuǎn)換的類型是否相同來判斷是否能夠進行向下轉(zhuǎn)換。
4、reinterpret_cast
幾乎什么都可以轉(zhuǎn),比如將int轉(zhuǎn)指針,可能會出問題,盡量少用;

20.成員初始化列表的概念,為什么用成員初始化列表會快一些。

在類構(gòu)造函數(shù)中,不在函數(shù)體內(nèi)對變量賦值,而在參數(shù)列表后,跟一個冒號和初始化列表。

①:成員類型是沒有默認構(gòu)造函數(shù)的類。若沒有提供顯示初始化,則類創(chuàng)建對象時會調(diào)用默認構(gòu)造函數(shù),如果沒有默認構(gòu)造函數(shù),則必須顯示初始化。

②:const成員或者引用類型的成員。因為const對象或者引用類型只能初始化,不能賦值。

為什么成員初始化列表效率更高?

因為對于非內(nèi)置類型,少了一次調(diào)用默認構(gòu)造函數(shù)的過程。

21.C++11新特性

nullptr, auto自動類型推導(dǎo),范圍for循環(huán),初始化列表, lambda表達式等

1.nullptr
C++11 引入了 nullptr 關(guān)鍵字,專門用來區(qū)分空指針和0
2.auto
auto用于從初始化表達式中推斷出變量的數(shù)據(jù)類型。注意:聲明的變量必須要初始化,否則編譯器不能判斷變量的類型。auto不能被聲明為返回值,auto不能作為形參,auto不能被修飾為模板參數(shù)
3.decltype 關(guān)鍵字
decltype 關(guān)鍵字是為了解決 auto 關(guān)鍵字只能對變量進行類型推導(dǎo)的缺陷而出現(xiàn)的。它的用法和 sizeof 很相似 ,decltype(表達式)
功能:編譯器分析表達式并得到它的類型,卻不實際計算表達式的值。
4.lambda表達式
lambda表達式是匿名函數(shù),可以認為是一個可執(zhí)行體functor

[捕獲區(qū)](參數(shù)區(qū)){代碼區(qū)};

? [a,&b] 其中 a 以復(fù)制捕獲而 b 以引用捕獲。
? [this] 以引用捕獲當(dāng)前對象( *this )
? [&] 以引用捕獲所有用于 lambda 體內(nèi)的自動變量,并以引用捕獲當(dāng)前對象,若存在
? [=] 以復(fù)制捕獲所有用于 lambda 體內(nèi)的自動變量,并以引用捕獲當(dāng)前對象,若存在
? [] 不捕獲,大部分情況下不捕獲就可以了
5. 右值引用與移動語義
左值和右值的區(qū)分標(biāo)準在于能否獲取地址。右值無法獲取地址,但不表示其不可改變,當(dāng)定義了右值的右值引用時就可以更改右值。
移動語義可以減少無謂的內(nèi)存拷貝,要想實現(xiàn)移動語義,需要實現(xiàn)移動構(gòu)造函數(shù)和移動賦值函數(shù)。
1.標(biāo)準庫move函數(shù)
根據(jù)右值引用的語法規(guī)則可知,不能將右值引用綁定到一個左值上,c++11引入右值引用,并且提供了move函數(shù),用來獲得綁定到左值上的右值引用。
Int &&iii = move(ii)
調(diào)用move之后,必須保證除了對ii復(fù)制或銷毀它外,我們將不再使用它,在調(diào)用move之后,我們不能對移動源后對象做任何假設(shè)。
std::forward是有條件轉(zhuǎn)換。只有在它的參數(shù)綁定到一個右值時,它才轉(zhuǎn)換它的參數(shù)到一個右值。當(dāng)參數(shù)綁定到左值時,轉(zhuǎn)換后仍為左值。
6. 智能指針
智能指針:行為類似于指針的類對象 ,它的一種通用實現(xiàn)方法是采用引用計數(shù)的方法。
1.智能指針將一個計數(shù)器與類指向的對象相關(guān)聯(lián),引用計數(shù)跟蹤共有多少個類對象共享同一指針。
2.每次創(chuàng)建類的新對象時,初始化指針并將引用計數(shù)置為1;
3.當(dāng)對象作為另一對象的副本而創(chuàng)建時,拷貝構(gòu)造函數(shù)拷貝指針并增加與之相應(yīng)的引用計數(shù);
4.對一個對象進行賦值時,賦值操作符減少左操作數(shù)所指對象的引用計數(shù)(如果引用計數(shù)為減至0,則刪除對象),并增加右操作數(shù)所指對象的引用計數(shù);這是因為左側(cè)的指針指向了右側(cè)指針?biāo)赶虻膶ο螅虼擞抑羔標(biāo)赶虻膶ο蟮囊糜嫈?shù)+1;
5.調(diào)用析構(gòu)函數(shù)時,構(gòu)造函數(shù)減少引用計數(shù)(如果引用計數(shù)減至0,則刪除基礎(chǔ)對象)。
1.unique_ptr
unique_ptr“唯一”擁有其所指對象,同一時刻只能有一個unique_ptr指向給定對象(通過禁止拷貝語義、只有移動語義來實現(xiàn))
2.shared_ptr
shared_ptr多個指針指向相同的對象。shared_ptr使用引用計數(shù),每一個shared_ptr的拷貝都指向相同的內(nèi)存。每使用他一次,內(nèi)部的引用計數(shù)加1,每析構(gòu)一次,內(nèi)部的引用計數(shù)減1,減為0時,自動刪除所指向的堆內(nèi)存。shared_ptr內(nèi)部的引用計數(shù)是線程安全的,但是對象的讀取需要加鎖。注意避免循環(huán)引用.
3.weak_ptr
weak_ptr是為了配合shared_ptr而引入的一種智能指針,因為它不具有普通指針的行為,它的最大作用在于協(xié)助shared_ptr工作,像旁觀者那樣觀測資源的使用情況。weak_ptr可以從一個shared_ptr或者另一個weak_ptr對象構(gòu)造,獲得資源的觀測權(quán)。但weak_ptr沒有共享資源,它的構(gòu)造不會引起指針引用計數(shù)的增加。

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

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

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