C++面试笔记(2)
11 explicit 顯式構(gòu)造函數(shù)
explicit修飾的構(gòu)造函數(shù)可用來防止隱式轉(zhuǎn)換
class Test1 { public:Test1(int n) // 普通構(gòu)造函數(shù){num=n;} private:int num; };class Test2 { public:explicit Test2(int n) // explicit(顯式)構(gòu)造函數(shù){num=n;} private:int num; };int main() {Test1 t1=12; // 隱式調(diào)用其構(gòu)造函數(shù),成功Test2 t2=12; // 編譯錯誤,不能隱式調(diào)用其構(gòu)造函數(shù)Test2 t2(12); // 顯式調(diào)用成功return 0;12.C++ 虛函數(shù) 重載 覆蓋之間的區(qū)別
C++ 虛函數(shù) 重載 重寫的區(qū)別
多態(tài)性可以簡單地概括為“一個接口,多種方法”,程序在運行時才決定調(diào)用的函數(shù)。Cpp多態(tài)性是通過虛函數(shù)實現(xiàn)的,虛函數(shù)允許子類重新定義成員函數(shù)。重寫的話分為兩種:直接重寫成員函數(shù),一個是重寫虛函數(shù),其中只有重寫了虛函數(shù)的才體現(xiàn)了Cpp的多態(tài)性。
多態(tài)與非多態(tài)的實質(zhì)區(qū)別就是函數(shù)地址是早綁定還是晚綁定。如果函數(shù)的調(diào)用,在編譯器編譯期間就可以確定函數(shù)的調(diào)用地址,并生產(chǎn)代碼,是靜態(tài)的,就是說地址是早綁定的。而如果函數(shù)調(diào)用的地址不能在編譯器期間確定,需要在運行時才確定,這就屬于晚綁定。
最常見的用法就是聲明基類的指針,利用該指針指向任意一個子類對象,調(diào)用相應(yīng)的虛函數(shù),可以根據(jù)指向的子類的不同而實現(xiàn)不同的方法。如果沒有使用虛函數(shù)的話,即沒有利用 C++ 多態(tài)性,則利用基類指針調(diào)用相應(yīng)的函數(shù)的時候,將總被限制在基類函數(shù)本身,而無法調(diào)用到子類中被重寫過的函數(shù)。因為沒有多態(tài)性,函數(shù)調(diào)用的地址將是一定的,而固定的地址將始終調(diào)用到同一個函數(shù),這就無法實現(xiàn)一個接口,多種方法的目的了。
13 早綁定與晚綁定
(1)靜態(tài)多態(tài)(早綁定)
函數(shù)重載
(2)動態(tài)多態(tài)(晚綁定)
虛函數(shù):用 virtual 修飾成員函數(shù),使其成為虛函數(shù)
注意:
普通函數(shù)(非類成員函數(shù))不能是虛函數(shù)
靜態(tài)函數(shù)(static)不能是虛函數(shù)
構(gòu)造函數(shù)不能是虛函數(shù)(因為在調(diào)用構(gòu)造函數(shù)時,虛表指針并沒有在對象的內(nèi)存空間中,必須要構(gòu)造函數(shù)調(diào)用完成后才會形成虛表指針)
***
14 虛析構(gòu)函數(shù)
虛析構(gòu)函數(shù)是為了解決基類的指針指向派生類對象,并用基類的指針刪除派生類對象
lass Shape { public:Shape(); // 構(gòu)造函數(shù)不能是虛函數(shù)virtual double calcArea();virtual ~Shape(); // 虛析構(gòu)函數(shù) }; class Circle : public Shape // 圓形類 { public:virtual double calcArea();... }; int main() {Shape * shape1 = new Circle(4.0);shape1->calcArea(); delete shape1; // 因為Shape有虛析構(gòu)函數(shù),所以delete釋放內(nèi)存時,先調(diào)用子類析構(gòu)函數(shù),再調(diào)用基類析構(gòu)函數(shù),防止內(nèi)存泄漏。shape1 = NULL;return 0; }15 虛函數(shù)與純虛函數(shù)
C++ Primer--虛函數(shù)與純虛函數(shù)的區(qū)別
定義一個函數(shù)為虛函數(shù),是為了允許用基類的指針調(diào)用子類的這個函數(shù)
定義一個函數(shù)為純虛函數(shù),才代表子類沒有被實現(xiàn)
定義純虛函數(shù)是為了實現(xiàn)一個接口,起到一個規(guī)范的作用,規(guī)范繼承這個類的程序員必須實現(xiàn)這個函數(shù)。
含有純虛函數(shù)的類為抽象類
任何構(gòu)造函數(shù)之外的非靜態(tài)函數(shù)都可以是虛函數(shù)
***
16. 虛函數(shù)的底層實現(xiàn)?虛函數(shù)表?虛函數(shù)指針?
簡述C++虛函數(shù)作用及底層實現(xiàn)原理
虛函數(shù)的作用:簡單講即實現(xiàn)了多態(tài)
基類定義了虛函數(shù),子類可以重寫該函數(shù),當(dāng)子類重新定義了父類的的虛函數(shù)后,父類指針根據(jù)賦給它的不同的子類指針,動態(tài)調(diào)用屬于子類的該函數(shù),且這樣的函數(shù)調(diào)用是無法在編譯器期間確認的,而是在運行期間確認,也叫晚綁定。
底層實現(xiàn)原理:C++對象模型
(1) 每個class產(chǎn)生一堆指向虛函數(shù)的指針,放在表格中,這個表格稱為虛函數(shù)表(vbtl)
(2) 每一個對象被添加了一個指針,指向相關(guān)的虛函數(shù)表vtbl。通常這個指針被稱為vptr。vptr的設(shè)定(setting)和重置(resetting)都由每一個class的構(gòu)造函數(shù),析構(gòu)函數(shù)和拷貝賦值運算符自動完成
(3)
另外,虛函數(shù)表地址的前面設(shè)置了一個指向type_info的指針,RTTI(Run Time Type Identification)運行時類型識別是有編譯器在編譯器生成的特殊類型信息,包括對象繼承關(guān)系,對象本身的描述,RTTI是為多態(tài)而生成的信息,所以只有具有虛函數(shù)的對象在會生成。
RTTI:運行時的類型識別
程序在運行時能夠根據(jù)基類的指針或者引用來獲得該指針或?qū)ο笏赶驅(qū)ο蟮膶嶋H類型
-- typeid:運算符返回其表達式或類型名的實際類型
-- dynamic_cast:將基類的指針或者引用安全地準(zhǔn)換為派生類型的指針或引用
C++ 虛函數(shù)表和虛函數(shù)指針機制
虛函數(shù)指針:在含有虛函數(shù)類的對象中,指向虛函數(shù)表,在運行時確定
虛函數(shù)表:在程序只讀數(shù)據(jù)段,存放虛函數(shù)指針,如果派生類實現(xiàn)了基類的某個虛函數(shù),則在許表中覆蓋原本基類的那個虛函數(shù)指針,在編譯時根據(jù)類的聲明創(chuàng)建
***
17. 菱形繼承?虛繼承?虛繼承與虛函數(shù)比較?
菱形繼承和虛繼承
菱形繼承(鉆石問題):
存在問題:浪費存儲空間、存在二義性
這時候虛繼承就挺身而出,扛下搞定此問題的重擔(dān)了。虛繼承是一種機制,類通過虛繼承指出它希望共享虛基類的狀態(tài)。對給定的虛基類,無論該類在派生層次中作為虛基類出現(xiàn)多少次,只繼承一個共享的基類子對象,共享基類子對象稱為虛基類。虛基類用 virtual 聲明繼承關(guān)系就行了。這樣一來,D 就只有 A 的一份拷貝。
class A { public:A():a(1){};void printA(){cout<<a<<endl;}int a; };class B : virtual public A { };class C : virtual public A { };class D: public B , public C { };int _tmain(int argc, _TCHAR* argv[]) {D d;cout<<sizeof(d);d.a=10;d.printA(); }相同之處:
都利用了虛指針(均占用類的存儲空間)和虛表(均不占用類的存儲空間)
不同之處:
虛繼承
虛基類依舊存在繼承類中,只占用存儲空間
虛基類表存儲的是虛基類相對直接繼承類的偏移
虛函數(shù)
虛函數(shù)不占用存儲空間
虛函數(shù)表存儲的是虛函數(shù)地址
18.智能指針
C++ 智能指針簡單剖析
智能指針的作用:防止內(nèi)存泄漏
auto_ptr(C++11中刪除了)
shared_ptr:允許多個指針指向同一個對象
unique_ptr:獨占所指的對象
weak_ptr: 弱引用,指向shared_ptr所管理的對象
為什么拋棄auto_ptr:避免潛在的內(nèi)存崩潰
為什么用weak_ptr:解決shared_ptr相互引用時的死鎖問題
C++11智能指針
智能指針是一個RAII(Resource Acquisition is initialization)類模型,用來動態(tài)的分配內(nèi)存。它提供所有普通指針提供的接口,卻很少發(fā)生異常。在構(gòu)造中,它分配內(nèi)存,當(dāng)離開作用域時,它會自動釋放已分配的內(nèi)存。這樣的話,程序員就從手動管理動態(tài)內(nèi)存的繁雜任務(wù)中解放出來了。
RAII(Resource Acquisition is Intialization)
RAII的做法是使用一個對象,在其構(gòu)造時獲取資源,在對象生命期控制對資源的訪問使之始終有效,最后在對象析構(gòu)時釋放資源。
分為常性類型與變性類型
本質(zhì):用對象代表資源,把管理資源的任務(wù)轉(zhuǎn)換為管理對象的任務(wù)
19.強制類型轉(zhuǎn)換運算符
Cpp四種強制類型轉(zhuǎn)換運算符
【Cpp專題】static_cast, dynamic_cast, const_cast探討
(1) static_cast
- 用于非多態(tài)類型的轉(zhuǎn)換
- 不執(zhí)行運行時類型檢查(轉(zhuǎn)換安全性不如 dynamic_cast)
- 通常用于轉(zhuǎn)換數(shù)值數(shù)據(jù)類型(如 float -> int)
- 可以在整個類層次結(jié)構(gòu)中移動指針,子類轉(zhuǎn)化為父類安全(向上轉(zhuǎn)換),父類轉(zhuǎn)化為子類不安全(因為子類可能有不在父類的字段或方法)
向上轉(zhuǎn)換是一種隱式轉(zhuǎn)換。
(2)dynamic_cast
- 用于多態(tài)類型的轉(zhuǎn)換
- 執(zhí)行行運行時類型檢查
- 只適用于指針或引用
- 對不明確的指針的轉(zhuǎn)換將失敗(返回 nullptr),但不引發(fā)異常
- 可以在整個類層次結(jié)構(gòu)中移動指針,包括向上轉(zhuǎn)換、向下轉(zhuǎn)換
(3) const_cast
- 用于刪除 const、volatile 和 __unaligned 特性(如將 const int 類型轉(zhuǎn)換為 int 類型 )
(4)reinterpret_cast
- 用于位的簡單重新解釋
- 濫用 reinterpret_cast 運算符可能很容易帶來風(fēng)險。 除非所需轉(zhuǎn)換本身是低級別的,否則應(yīng)使用其他強制轉(zhuǎn)換運算符之一。
- 允許將任何指針轉(zhuǎn)換為任何其他指針類型(如 char* 到 int* 或 One_class* 到 Unrelated_class* 之類的轉(zhuǎn)換,但其本身并不安全)
也允許將任何整數(shù)類型轉(zhuǎn)換為任何指針類型以及反向轉(zhuǎn)換。 - reinterpret_cast 運算符不能丟掉 const、volatile 或 __unaligned 特性。
- reinterpret_cast 的一個實際用途是在哈希函數(shù)中,即,通過讓兩個不同的值幾乎不以相同的索引結(jié)尾的方式將值映射到索引。
***
轉(zhuǎn)載于:https://www.cnblogs.com/jeremy0426/p/9509389.html
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的C++面试笔记(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OI常用的常数优化小技巧
- 下一篇: s3c2440移植MQTT