C++ 面試常被詢問基本觀念(2021/06/29)
C++ 面試常被詢問基本觀念(2021/06/29)
資料來源: https://mp.weixin.qq.com/s/_lHkzk1D235BQrDVkiGTtw
1.指針(指標)和引用的區別
指針指向一塊內存,它的內容是內存中的地址,而引用則是某個內存的別名,它的指向不改變。
本質區別:指針有自己的一塊空間(地址),而引用只是一個別名。
使用sizeof看一個指針的大小是4(64位是8),而引用是被引用對象的大小。指針可以被初始化為NULL,而引用必須被初始化且必須是一個已有對象的引用。可以有const指針,但是沒有const引用。指針在使用中可以指向其他對象,但是引用還只能是一個對象的引用,不能被改變。如果返回動態內存分配的對像或者內存,必須使用指針,引用可能引起內存洩漏。
2.智能指針
c++裡面的四個智能指針: auto_ptr, shared_ptr, weak_ptr, unique_ptr
3.多態的作用、多態機制
接口的多種不同的實現方式即為多態。多態性是允許你將父對象設置成為一個或更多的他的子對象相等的技術。多態就是當發出一條命令時,不同的對象接收到同樣的命令後所做出的動作是不同的。
機制:多態通常有兩種實現方法:子類繼承父類(extends)、類實現接口(implements);無論是哪種方法,其核心之處就在於對父類方法的改寫或對接口方法的實現,以取得在運行時不同的執行效果。要使用多態,在聲明對象時就應該遵循一條法則:聲明的總是父類類型或接口類型,創建的是實際類型。
作用:多態最大的用途我認為在於對設計和架構的複用。
4.虛函數
虛函數:實現類的多態性。
定義一個函數為虛函數,不代表函數為不被實現的函數。定義他為虛函數是為了允許用基類的指針來調用子類的這個函數。定義一個函數為純虛函數,才代表函數沒有被實現。
C++中的虛函數的作用主要是實現了多態的機制。基類定義虛函數,子類可以重寫該函數;在派生類中對基類定義的虛函數進行重寫時,需要在派生類中聲明該方法為虛方法。
C++裡調用虛函數比調用普通函數慢:普通函數快,因為地址在編譯期間指定,單純的尋址調用。虛函數調用時,首先找虛函數表,然後找偏移地址進行調用。
5.虛函數表放在哪裡,虛表指針什麼時候初始化
虛函數表和靜態成員變量一樣,存放在全局數據區.
在構造函數中進行虛表的創建和虛表指針的初始化。
6.純虛函數(純虛函數在子類中沒有實現會不會報錯:會)
純虛函數是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。在基類中實現純虛函數的方法是在函數原型後加=0:
定義純虛函數是為了實現一個接口,起到一個規範的作用,規范繼承這個類的程序員必須實現這個函數。
7.虛函數和純虛函數的區別
含有純虛函數的類稱為抽像類,只含有虛函數的類不能稱為抽像類。虛函數可以直接被使用,也可以被子類重載以後以多態形式調用,而純虛函數必須在子類中實現該函數才可使用,因為純虛函數在基類中只有聲明而沒有定義。虛函數必須實現,對虛函數來說父類和子類都有各自的版本。
8.繼承是什麼意思?
繼承是指一個對象直接使用另一對象的屬性和方法。
繼承機制是面向對象程序設計使代碼可以復用的最重要的手段,它允許程序員在保持原有的特性基礎上進行擴展,增加功能,這樣產生新的類,稱作是派生類。繼承呈現了面向對象程序設計的層析結構,體現了由簡單到復雜的認知過程。繼承是類設計層次的複用。
9.C++的繼承和Java繼承的區別?
c++支持多重繼承,java不支持多繼承;
虛繼承:當我們在繼承方式前加virtual關鍵字,進行虛繼承,虛繼承內存中會通過虛基類指針指向虛基類表,該表中的數據是為虛基類指針到基類的存儲區域的偏移值。
虛繼承:主要解決內存重複的問題,同時避免訪問衝突。
10.枚舉類能否繼承
枚舉的作用:限定“數據集”中的元素的個數(將類理解為一個集合)、即限定枚舉類對象的個數。枚舉類也是類,也可以有自己的成員變量,成員方法,靜態方法、靜態變量等,也能實現其他的接口,不能繼承其他類了。
11.類中繼承的方式,為什麼會有這幾種方式,作用是什麼
public 繼承:父類成員在子類中保持原有訪問級別;
private 繼承:父類成員在子類中變為私有成員;
protected 繼承:父類中的公有成員變為保護成員,其它成員保持不變;
12.深拷貝淺拷貝區別
假設B複製了A,修改A的時候,看B是否發生變化:如果B跟著也變了,說明是淺拷貝,拿人手短!(修改堆內存中的同一個值);如果B沒有改變,說明是深拷貝,自食其力!(修改堆內存中的不同的值)。
淺拷貝(shallowCopy)只是增加了一個指針指向已存在的內存地址,深拷貝(deepCopy)是增加了一個指針並且申請了一個新的內存,使這個增加的指針指向這個新的內存。
區別:深複製和淺複製最根本的區別在於是否是真正獲取了一個對象的複制實體,而不是引用。淺複製—-只是拷貝了基本類型的數據,而引用類型數據,複製後也是會發生引用,我們把這種拷貝叫做“(淺複製)淺拷貝”,換句話說,淺複製僅僅是指向被複製的內存地址,如果原地址中對像被改變了,那麼淺複製出來的對像也會相應改變。深複製—-在計算機中開闢了一塊新的內存地址用於存放複製的對象。
13.拷貝和賦值的區別
拷貝分為:賦值,淺拷貝,深拷貝,拷貝程度一次遞增;賦值:賦值就是相當於做了個軟連接,所以不管你是修改了鏈接文聯的內容還是源文件的內容,這個文件都會有所改變(相當於鏡子裡的自己,你動他也動).淺拷貝:只是做了部分的拷貝,何謂部分拷貝?就是在賦值的基礎上減去了列表最外層的鏈接,其他的和賦值基本一樣.深拷貝:相當於把文件複製了一份,新文件或老文件的改變都是互不相干的,完全獨立於老文件。
14. int func(int *a, int *b) 和int func(int a, int b)的區別
一個用指針做參數,一個用自變量做參數。
15.面向對象C++的三個特性
封裝:把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對像操作,對不可信的進行信息隱藏。
繼承:面向對象編程(OOP) 語言的一個主要功能就是“繼承”。繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展。
多態:多態性(polymorphisn)是允許你將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。
16. C++類構造函數和析構函數
構造函數是一個特殊的公共成員函數,它在創建類對象時會自動被調用,用於構造類對象。
類的構造函數是類的一種特殊的成員函數,它會在每次創建類的新對象時執行。構造函數的名稱與類的名稱是完全相同的,並且不會返回任何類型,也不會返回void。構造函數可用於為某些成員變量設置初始值。
類的析構函數是類的一種特殊的成員函數,它會在每次刪除所創建的對象時執行。析構函數的名稱與類的名稱是完全相同的,只是在前面加了個波浪號(~)作為前綴,它不會返回任何值,也不能帶有任何參數。析構函數有助於在跳出程序(比如關閉文件、釋放內存等)前釋放資源。
17.構造函數和析構函數的作用
構造函數的作用:用於新建對象的初始化工作。
析構函數的作用:用於在撤銷對象前,完成一些清理工作,比如:釋放內存等。
每當創建對象時,需要添加初始化代碼時,則需要定義自己的構造函數;
而對象撤銷時,需要自己添加清理工作的代碼時,則需要定義自己的析構函數。
18.C++中虛析構函數的作用
虛析構函數是為了避免內存洩露,而且是當子類中會有指針成員變量時才會使用得到的。也就說虛析構函數使得在刪除指向子類對象的基類指針時可以調用子類的析構函數達到釋放子類中堆內存的目的,而防止內存洩露的。
19. C++中malloc和new的區別
1)malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。它們都可用於申請動態內存和釋放內存。
2)對於非內部數據類型的對象而言,光用maloc/free無法滿足動態對象的要求。對像在創建的同時要自動執行構造函數,對像在消亡之前要自動執行析構函數。由於malloc/free是庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free。
3)因此C++語言需要一個能完成動態內存分配和初始化工作的運算符new,以一個能完成清理與釋放內存工作的運算符delete。注意new/delete不是庫函數。
4)C++程序經常要調用C函數,而C程序只能用malloc/free管理動態內存。
20. C++中struct和class的區別
對於成員訪問權限以及繼承方式,class默認都是private,struct默認是public;class可以用於表示模板類型,struct不行;一般來說,用到繼承時常用class,沒用到繼承時則使用struct。
21. C++ static、const
Static:如果某個屬性為整個類所共有,不屬於任何一個具體對象,則採用static 關鍵字來聲明靜態成員。
Const:常對像是這樣的對象:它的數據成員值在對下崗的整個生存週期內不能被改變,也就是說,常對象必須進行初始化而且不能被更新!
什麼時候用static:需要一個數據對象為整個類而非某個對象服務,同時又力求不破壞類的封裝性,即要求此成員隱藏在類的內部,對外不可見。
static關鍵字的作用:常用來修飾變量。全局變量被static修飾後,就稱之為靜態全局變量;局部變量被static修飾後,就稱之為靜態局部變量。統稱為靜態變量。
22. const和#define的區別
①編譯器處理方式不同:define宏是在預處理階段展開,const常量是編譯運行階段使用。
②類型和安全檢查不同:define宏沒有數據類型,不做任何類型檢查,僅僅是展開。const常量有具體的數據類型,在編譯階段會執行類型檢查。③存儲方式不同:define宏僅僅是展開,有多少地方使用,就展開多少次,不會分配內存。const常量會在內存中分配(可以是堆中也可以是棧中)。④代碼調試不同:const常量可以進行調試的。define是不能進行調試的,因為在預編譯階段就已經替換掉了。
23.C++的棧區和堆區知道嗎?(棧區是存儲函數內部變量的內存區,堆區是存動態申請的內存)
24. C++裡面vector、list、deque的區別
vector:vector是連續內存空間,支持高效的隨機存取和尾部插入刪除,對其他位置插入刪除不方便,對比數組是可以自由動態增加空間。(偽動態,也是構造新一片內存空間,拷貝舊空間數據到新空間,釋放舊空間)
deque:邏輯上的連續內存空間,支持高效的隨機存取和頭尾部雙端插入刪除。在中間進行插入刪除也需要進行大量的數據移動。
List:雙向循環鍊錶,支持高效的插入和刪除,對隨機存取不方便。
如果想要高效的存取,不在乎插入和刪除的效率,選用vector.
如果需要大量的刪除和插入操作,不關心隨機存取,選用list
如果需要高效的存取,同時在乎首尾的插入和刪除,選用deque.
25.struct和union有什麼區別
①共用體和結構體都是由多個不同的數據類型成員組成, 但在任何同一時刻, 共用體只存放了一個被選中的成員, 而結構體的所有成員都存在。
②對於共用體的不同成員賦值, 將會對其它成員重寫, 原來成員的值就不存在了, 而對於結構體的不同成員賦值是互不影響的。
26. C++中include時如何保證不重複加載頭文件
#ifndef方式、#pragma once方式
27.什麼時候要進行動態內存申請?
當無法事先確定對象需要使用多少內存(這些對象所需的內存大小只有在程序運行的時候才能確定)時就要申請動態內存,比如維護一個動態增長的鍊錶或樹。
28.C++內存洩漏和野指針怎麼解決
內存洩漏:①訪問已經釋放的內存 ②訪問沒有權限的內存。
野指針:指向內存被釋放的內存或者沒有訪問權限的內存的指針。
野指針可以使用shared_ptr和weak_ptr結合使用來盡量規避,釋放內存後把指針指向NULL,防止指針在後面不小心又被解引用了。
內存洩漏的解決:使用智能指針。因為智能指針可以自動刪除分配的內存。
29.C++模板的作用
模板(Template)指C++程序設計設計語言中採用類型作為參數的程序設計,支持通用程序設計。通常有兩種形式:函數模板和類模板;