C ?八股文(一)
時(shí)間:2021-09-06 15:21:21
手機(jī)看文章
掃描二維碼
隨時(shí)隨地手機(jī)看文章
[導(dǎo)讀]多態(tài)什么是多態(tài),有什么用C多態(tài)有兩種:靜態(tài)多態(tài)(早綁定)、動(dòng)態(tài)多態(tài)(晚綁定)。靜態(tài)多態(tài)是通過(guò)函數(shù)重載實(shí)現(xiàn)的;動(dòng)態(tài)多態(tài)是通過(guò)虛函數(shù)實(shí)現(xiàn)的。定義:“一個(gè)接口,多種方法”,程序在運(yùn)行時(shí)才決定要調(diào)用的函數(shù)。實(shí)現(xiàn):C多態(tài)性主要是通過(guò)虛函數(shù)實(shí)現(xiàn)的,虛函數(shù)允許子類(lèi)重寫(xiě)override(注意和o...
多態(tài)
什么是多態(tài),有什么用
C 多態(tài)有兩種:靜態(tài)多態(tài)(早綁定)、動(dòng)態(tài)多態(tài)(晚綁定)。靜態(tài)多態(tài)是通過(guò)函數(shù)重載實(shí)現(xiàn)的;動(dòng)態(tài)多態(tài)是通過(guò)虛函數(shù)實(shí)現(xiàn)的。- 定義:“一個(gè)接口,多種方法”,程序在運(yùn)行時(shí)才決定要調(diào)用的函數(shù)。
- 實(shí)現(xiàn):C 多態(tài)性主要是通過(guò)虛函數(shù)實(shí)現(xiàn)的,虛函數(shù)允許子類(lèi)重寫(xiě) override(注意和 overload 的區(qū)別,overload 是重載,是允許同名函數(shù)的表現(xiàn),這些函數(shù)參數(shù)列表/類(lèi)型不同)。
- 目的:接口重用。封裝可以使得代碼模塊化,繼承可以擴(kuò)展已存在的代碼,他們的目的都是為了代碼重用。而多態(tài)的目的則是為了接口重用。
- 用法:聲明基類(lèi)的指針,利用該指針指向任意一個(gè)子類(lèi)對(duì)象,調(diào)用相應(yīng)的虛函數(shù),可以根據(jù)指向的子類(lèi)的不同而實(shí)現(xiàn)不同的方法。
重寫(xiě)、重載與隱藏的區(qū)別
Overload 重載
在 C 程序中,可以將語(yǔ)義、功能相似的幾個(gè)函數(shù)用同一個(gè)名字表示,但參數(shù)或返回值不同(包括類(lèi)型、順序不同),即函數(shù)重載。- 相同的范圍(在同一個(gè)類(lèi)中);
- 函數(shù)名字相同;
- 參數(shù)不同;
- virtual 關(guān)鍵字可有可無(wú);
Override(覆蓋或重寫(xiě))
是指派生類(lèi)函數(shù)覆蓋基類(lèi)函數(shù),特征是:- 不同的范圍(分別位于派生類(lèi)與基類(lèi));
- 函數(shù)名字相同;參數(shù)相同;
- 基類(lèi)函數(shù)必須有 virtual 關(guān)鍵字。
Overwrite(重寫(xiě))隱藏,
是指派生類(lèi)的函數(shù)屏蔽了與其同名的基類(lèi)函數(shù),規(guī)則如下:- 如果派生類(lèi)的函數(shù)與基類(lèi)的函數(shù)同名,但是參數(shù)不同。此時(shí),不論有無(wú) virtual 關(guān)鍵字,基類(lèi)的函數(shù)將被隱藏(注意別與重載混淆)。
- 如果派生類(lèi)的函數(shù)與基類(lèi)的函數(shù)同名,并且參數(shù)也相同,但是基類(lèi)函數(shù)沒(méi)有 virtual 關(guān)鍵字。此時(shí),基類(lèi)的函數(shù)被隱藏(注意別與覆蓋混淆)。
虛函數(shù)和純虛函數(shù)
- 虛函數(shù):為了實(shí)現(xiàn)動(dòng)態(tài)綁定。使用基類(lèi)的引用或指針調(diào)用虛函數(shù)的時(shí)候會(huì)發(fā)生動(dòng)態(tài)綁定。
- 純虛函數(shù):抽象類(lèi)
- 構(gòu)造函數(shù)可以重載,但不能是虛函數(shù),析構(gòu)函數(shù)可以是虛函數(shù)。
基類(lèi)為什么需要虛析構(gòu)函數(shù)?
防止內(nèi)存泄漏。想去借助父類(lèi)指針去銷(xiāo)毀子類(lèi)對(duì)象的時(shí)候,不能去銷(xiāo)毀子類(lèi)對(duì)象。假如沒(méi)有虛析構(gòu)函數(shù),釋放一個(gè)由基類(lèi)指針指向的派生類(lèi)對(duì)象時(shí),不會(huì)觸發(fā)動(dòng)態(tài)綁定,則只會(huì)調(diào)用基類(lèi)的析構(gòu)函數(shù),不會(huì)調(diào)用派生類(lèi)的。派生類(lèi)中申請(qǐng)的空間則得不到釋放導(dǎo)致內(nèi)存泄漏。構(gòu)造/析構(gòu)函數(shù)調(diào)用虛函數(shù)
派生類(lèi)對(duì)象構(gòu)造期間進(jìn)入基類(lèi)的構(gòu)造函數(shù)時(shí),對(duì)象類(lèi)型變成了基類(lèi)類(lèi)型,而不是派生類(lèi)類(lèi)型。同樣,進(jìn)入基類(lèi)析構(gòu)函數(shù)時(shí),對(duì)象也是基類(lèi)類(lèi)型。所以,虛函數(shù)始終僅僅調(diào)用基類(lèi)的虛函數(shù)(如果是基類(lèi)調(diào)用虛函數(shù)),不能達(dá)到多態(tài)的效果。虛函數(shù)表
- 產(chǎn)生時(shí)間:編譯期
- 存儲(chǔ)位置:只讀數(shù)據(jù)段 .rodata
- 虛指針:類(lèi)的每一個(gè)對(duì)象都包含一個(gè)虛指針(指向虛表),存在對(duì)象實(shí)例的最前面四個(gè)字節(jié)
- 虛指針創(chuàng)建時(shí)間:構(gòu)造函數(shù)
const 相關(guān)
如何初始化 const 和 static 數(shù)據(jù)成員?
通常在類(lèi)外申明 static 成員,但是 static const 的整型( bool,char,int,long )可以在類(lèi)中聲明且初始化,static const 的其他類(lèi)型必須在類(lèi)外初始化(包括整型數(shù)組)。static 和 const 分別怎么用,類(lèi)里面 static 和 const 可以同時(shí)修飾成員函數(shù)嗎?
static 的作用:對(duì) static 的三條作用做一句話總結(jié)。首先 static 的最主要功能是隱藏,其次因?yàn)?static 變量存放在靜態(tài)存儲(chǔ)區(qū),所以它具備持久性和默認(rèn)值 0。對(duì)變量
局部變量
在局部變量之前加上關(guān)鍵字 static,局部變量就被定義成為一個(gè)局部靜態(tài)變量。- 內(nèi)存中的位置:靜態(tài)存儲(chǔ)區(qū)
- 初始化:未經(jīng)初始化的全局靜態(tài)變量會(huì)被程序自動(dòng)初始化為0(自動(dòng)對(duì)象的值是任意的,除非他被顯示初始化)
- 作用域:作用域仍為局部作用域,當(dāng)定義它的函數(shù)或者語(yǔ)句塊結(jié)束的時(shí)候,作用域隨之結(jié)束。
全局變量
在全局變量之前加上關(guān)鍵字 static,全局變量就被定義成為一個(gè)全局靜態(tài)變量。- 內(nèi)存中的位置:靜態(tài)存儲(chǔ)區(qū)(靜態(tài)存儲(chǔ)區(qū)在整個(gè)程序運(yùn)行期間都存在)
- 初始化:未經(jīng)初始化的全局靜態(tài)變量會(huì)被程序自動(dòng)初始化為 0(自動(dòng)對(duì)象的值是任意的,除非他被顯示初始化)
- 作用域:全局靜態(tài)變量在聲明他的文件之外是不可見(jiàn)的。準(zhǔn)確地講從定義之處開(kāi)始到文件結(jié)尾。
- 不會(huì)被其他文件所訪問(wèn),修改
- 其他文件中可以使用相同名字的變量,不會(huì)發(fā)生沖突。對(duì)全局函數(shù)也是有隱藏作用。而普通全局變量只要定義了,任何地方都能使用,使用前需要聲明所有的 .c 文件,只能定義一次普通全局變量,但是可以聲明多次(外部鏈接)。
對(duì)類(lèi)
成員變量
用 static 修飾類(lèi)的數(shù)據(jù)成員實(shí)際使其成為類(lèi)的全局變量,會(huì)被類(lèi)的所有對(duì)象共享,包括派生類(lèi)的對(duì)象。因此,static 成員必須在類(lèi)外進(jìn)行初始化(初始化格式:int base::var=10;),而不能在構(gòu)造函數(shù)內(nèi)進(jìn)行初始化,不過(guò)也可以用 const 修飾 static 數(shù)據(jù)成員在類(lèi)內(nèi)初始化 。因?yàn)殪o態(tài)成員屬于整個(gè)類(lèi),而不屬于某個(gè)對(duì)象,如果在類(lèi)內(nèi)初始化,會(huì)導(dǎo)致每個(gè)對(duì)象都包含該靜態(tài)成員,這是矛盾的。特點(diǎn):- 不要試圖在頭文件中定義(初始化)靜態(tài)數(shù)據(jù)成員。在大多數(shù)的情況下,這樣做會(huì)引起重復(fù)定義這樣的錯(cuò)誤。即使加上
#ifndef
#define
#endif
或者#pragma once
也不行。 - 靜態(tài)數(shù)據(jù)成員可以成為成員函數(shù)的可選參數(shù),而普通數(shù)據(jù)成員則不可以。
- 靜態(tài)數(shù)據(jù)成員的類(lèi)型可以是所屬類(lèi)的類(lèi)型,而普通數(shù)據(jù)成員則不可以。普通數(shù)據(jù)成員的只能聲明為 所屬類(lèi)類(lèi)型的指針或引用。
成員函數(shù)
- 用 static 修飾成員函數(shù),使這個(gè)類(lèi)只存在這一份函數(shù),所有對(duì)象共享該函數(shù),不含 this 指針。
- 靜態(tài)成員是可以獨(dú)立訪問(wèn)的,也就是說(shuō),無(wú)須創(chuàng)建任何對(duì)象實(shí)例就可以訪問(wèn)。
base::func(5,3)
;當(dāng) static 成員函數(shù)在類(lèi)外定義時(shí)不需要加 static 修飾符。 - 在靜態(tài)成員函數(shù)的實(shí)現(xiàn)中不能直接引用類(lèi)中說(shuō)明的非靜態(tài)成員,可以引用類(lèi)中說(shuō)明的靜態(tài)成員。因?yàn)殪o態(tài)成員函數(shù)不含this指針。
- 限定變量為不可修改。
- 限定成員函數(shù)不可以修改任何數(shù)據(jù)成員。
- const 與指針:
const char *p
常量指針,可以換方向,不可以改內(nèi)容char * const p
,指針常量,不可以換方向,可以改內(nèi)容構(gòu)造函數(shù)
構(gòu)造函數(shù)調(diào)用順序
- 虛基類(lèi)構(gòu)造函數(shù)(被繼承的順序)
- 非虛基類(lèi)構(gòu)造函數(shù)(被繼承的順序)
- 成員對(duì)象構(gòu)造函數(shù)(聲明順序)
- 自己的構(gòu)造函數(shù)
自身構(gòu)造函數(shù)順序
- 虛表指針(防止初始化列表里面調(diào)用虛函數(shù),否則調(diào)用的是父類(lèi)的虛函數(shù))
- 初始化列表(const、引用、沒(méi)有定義默認(rèn)構(gòu)造函數(shù)的類(lèi)型)
- 花括號(hào)里的 (初始化列表直接初始化,這個(gè)先初始化后賦值)
this 指針
創(chuàng)建時(shí)間:成員函數(shù)調(diào)用前生成,調(diào)用后清除如何傳遞給成員函數(shù):通過(guò)函數(shù)參數(shù)的首參數(shù)來(lái)傳遞extern 關(guān)鍵字
- 置于變量或者函數(shù)前,以標(biāo)示變量或者函數(shù)的定義在別的文件中,提示編譯器遇到此變量和函數(shù)時(shí)在其他模塊中尋找其定義
- extern “C” void fun(); 告訴編譯器按C的規(guī)則去翻譯
以下關(guān)鍵字的作用?使用場(chǎng)景?
- inline:在 c/c 中,為了解決一些頻繁調(diào)用的小函數(shù)大量消耗??臻g(棧內(nèi)存)的問(wèn)題,特別的引入了 inline 修飾符,表示為內(nèi)聯(lián)函數(shù)。
- decltype:從表達(dá)式中推斷出要定義變量的類(lèi)型,但卻不想用表達(dá)式的值去初始化變量。還有可能是函數(shù)的返回類(lèi)型為某表達(dá)式的的值類(lèi)型。
- volatile:volatile 關(guān)鍵字是一種類(lèi)型修飾符,用它聲明的類(lèi)型變量表示可以被某些編譯器未知的因素更改,比如:操作系統(tǒng)、硬件或者其它線程等。遇到這個(gè)關(guān)鍵字聲明的變量,編譯器對(duì)訪問(wèn)該變量的代碼就不再進(jìn)行優(yōu)化,從而可以提供對(duì)特殊地址的穩(wěn)定訪問(wèn)。
淺拷貝與深拷貝
什么時(shí)候用到拷貝函數(shù)?- 一個(gè)對(duì)象以值傳遞的方式傳入函數(shù)體(參數(shù));
- 一個(gè)對(duì)象以值傳遞的方式從函數(shù)返回(返回值);
- 一個(gè)對(duì)象需要通過(guò)另外一個(gè)對(duì)象進(jìn)行初始化(初始化)。
C 類(lèi)中成員初始化順序
成員變量在使用初始化列表初始化時(shí),與構(gòu)造函數(shù)中初始化成員列表的順序無(wú)關(guān),只與定義成員變量的順序有關(guān)。類(lèi)中 const 成員常量必須在構(gòu)造函數(shù)初始化列表中初始化。類(lèi)中 static 成員變量,只能在類(lèi)外初始化(同一類(lèi)的所有實(shí)例共享靜態(tài)成員變量)。構(gòu)造過(guò)程
- 分配內(nèi)存
- 進(jìn)行父類(lèi)的構(gòu)造,按照父類(lèi)的聲明順序(遞歸過(guò)程)
- 構(gòu)造虛表指針,對(duì)虛表指針賦值
- 根據(jù)初始化列表中的值初始化變量
- 執(zhí)行構(gòu)造函數(shù){}內(nèi)的
構(gòu)造函數(shù)初始化列表
const 或引用類(lèi)型的成員。因?yàn)?const 對(duì)象或引用類(lèi)型只能初始化,不能對(duì)他們賦值。與對(duì)數(shù)據(jù)成員賦值的區(qū)別:- 內(nèi)置數(shù)據(jù)類(lèi)型,復(fù)合類(lèi)型(指針,引用):結(jié)果和性能上相同。
- 用戶定義類(lèi)型(類(lèi)類(lèi)型):結(jié)果上相同,但是性能上存在很大的差別。
vector 中 size() 和 capacity() 的區(qū)別
size() 指容器當(dāng)前擁有的元素個(gè)數(shù)(對(duì)應(yīng)的resize(size_type)
會(huì)在容器尾添加或刪除一些元素,來(lái)調(diào)整容器中實(shí)際的內(nèi)容,使容器達(dá)到指定的大小。);capacity()
指容器在必須分配存儲(chǔ)空間之前可以存儲(chǔ)的元素總數(shù)。size 表示的這個(gè) vector 里容納了多少個(gè)元素,capacity 表示 vector 能夠容納多少元素,它們的不同是在于 vector 的 size 是 2 倍增長(zhǎng)的。如果 vector 的大小不夠了,比如現(xiàn)在的 capacity 是 4,插入到第五個(gè)元素的時(shí)候,發(fā)現(xiàn)不夠了,此時(shí)會(huì)給他重新分配 8 個(gè)空間,把原來(lái)的數(shù)據(jù)及新的數(shù)據(jù)復(fù)制到這個(gè)新分配的空間里。(會(huì)有迭代器失效的問(wèn)題)定義一個(gè)空類(lèi)編譯器做了哪些操作
如果你只是聲明一個(gè)空類(lèi),不做任何事情的話,編譯器會(huì)自動(dòng)為你生成一個(gè)默認(rèn)構(gòu)造函數(shù)、一個(gè)拷貝默認(rèn)構(gòu)造函數(shù)、一個(gè)默認(rèn)拷貝賦值操作符和一個(gè)默認(rèn)析構(gòu)函數(shù)。這些函數(shù)只有在第一次被調(diào)用時(shí),才會(huì)被編譯器創(chuàng)建。所有這些函數(shù)都是 inline 和 public 的。強(qiáng)制類(lèi)型轉(zhuǎn)換
static_cast用法:static_cast < type-id > ( expression )
q1. 為什么需要 static_cast 強(qiáng)制轉(zhuǎn)換?- void指針->其他類(lèi)型指針 (不安全)
- 改變通常的標(biāo)準(zhǔn)轉(zhuǎn)換
- 用于類(lèi)層次結(jié)構(gòu)中基類(lèi)和子類(lèi)之間指針或引用的轉(zhuǎn)換。進(jìn)行上行轉(zhuǎn)換(把子類(lèi)的指針或引用轉(zhuǎn)換成基類(lèi)表示)是安全的;進(jìn)行下行轉(zhuǎn)換(把基類(lèi)指針或引用轉(zhuǎn)換成子類(lèi)指針或引用)時(shí),由于沒(méi)有動(dòng)態(tài)類(lèi)型檢查,所以是不安全的。
dynamic_cast < type-id > ( expression )
dynamic_cast 主要用于類(lèi)層次間的上行轉(zhuǎn)換和下行轉(zhuǎn)換,還可以用于類(lèi)之間的交叉轉(zhuǎn)換(同一基類(lèi)的兩個(gè)同級(jí)派生類(lèi))。在類(lèi)層次間進(jìn)行上行轉(zhuǎn)換時(shí),dynamic_cast
和static_cast
的效果是一樣的;在進(jìn)行下行轉(zhuǎn)換時(shí),dynamic_cast
具有類(lèi)型檢查的功能,比static_cast
更安全。reinpreter_cast它可以把一個(gè)指針轉(zhuǎn)換成一個(gè)整數(shù),也可以把一個(gè)整數(shù)轉(zhuǎn)換成一個(gè)指針(先把一個(gè)指針轉(zhuǎn)換成一個(gè)整數(shù),在把該整數(shù)轉(zhuǎn)換成原類(lèi)型的指針,還可以得到原先的指針值)。const_cast該運(yùn)算符用來(lái)修改類(lèi)型的 const 或 volatile 屬性。除了 const ?或 volatile 修飾之外, type_id 和 expression 的類(lèi)型是一樣的。常量指針被轉(zhuǎn)化成非常量指針,并且仍然指向原來(lái)的對(duì)象;常量引用被轉(zhuǎn)換成非常量引用,并且仍然指向原來(lái)的對(duì)象;常量對(duì)象被轉(zhuǎn)換成非常量對(duì)象。volatile 關(guān)鍵字- 使用方法:
int volatile x
; - 作用:編譯器不再優(yōu)化。讓編譯器每次操作該變量時(shí)一定要從內(nèi)存中真正取出,而不是使用已經(jīng)存在寄存器中的值 。
內(nèi)存管理
C 內(nèi)存分配
- malloc:在內(nèi)存的動(dòng)態(tài)分配區(qū)域中分配一個(gè)長(zhǎng)度為 size 的連續(xù)空間,如果分配成功,則返回所分配內(nèi)存空間的首地址,否則返回 NULL,申請(qǐng)的內(nèi)存不會(huì)初始化。
- calloc:分配一個(gè) num * size 連續(xù)的空間,會(huì)自動(dòng)初始化為0。
- realloc:動(dòng)態(tài)分配一個(gè)長(zhǎng)度為 size 的內(nèi)存空間,并把內(nèi)存空間的首地址賦值給 ptr,把 ptr 內(nèi)存空間調(diào)整為 size。
C 內(nèi)存分配:
-棧區(qū)(stack):主要存放函數(shù)參數(shù)以及局部變量,由系統(tǒng)自動(dòng)分配釋放。- 堆區(qū)(heap):由用戶通過(guò) malloc/new 手動(dòng)申請(qǐng),手動(dòng)釋放。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類(lèi)似于鏈表。
- 全局/靜態(tài)區(qū):存放全局變量、靜態(tài)變量;程序結(jié)束后由系統(tǒng)釋放。- - 字符串常量區(qū):字符串常量就放在這里,程序結(jié)束后由系統(tǒng)釋放。
- 代碼區(qū):存放程序的二進(jìn)制代碼。
結(jié)構(gòu)體字節(jié)對(duì)齊問(wèn)題?結(jié)構(gòu)體/類(lèi)大小的計(jì)算?
默認(rèn)字節(jié)對(duì)齊
各成員變量存放的起始地址相對(duì)于結(jié)構(gòu)的起始地址的偏移量必須是該變量的類(lèi)型所占用的字節(jié)數(shù)的倍數(shù),結(jié)構(gòu)的大小為結(jié)構(gòu)的字節(jié)邊界數(shù)(即該結(jié)構(gòu)中占用最大空間的類(lèi)型所占用的字節(jié)數(shù))的倍數(shù) n 字節(jié)對(duì)齊。pragma pack(n)
- 如果 n 大于等于該變量所占用的字節(jié)數(shù),那么偏移量必須滿足默認(rèn)的對(duì)齊方式;
- 如果 n 小于該變量的類(lèi)型所占用的字節(jié)數(shù),那么偏移量為 n 的倍數(shù),不用滿足默認(rèn)的對(duì)齊方式;
- 如果 n 大于所有成員變量類(lèi)型所占用的字節(jié)數(shù),那么結(jié)構(gòu)體的總大小必須為占用空間最大的變量占用的空間數(shù)的倍數(shù);否則必須為n的倍數(shù)(兩者相比,取?。?/li>
虛函數(shù)的大小計(jì)算
假設(shè)經(jīng)過(guò)成員對(duì)齊后的類(lèi)的大小為 size 個(gè)字節(jié)。那么類(lèi)的 sizeof 大小可以這么計(jì)算:size 4*(虛函數(shù)指針的個(gè)數(shù) n)。聯(lián)合體的大小計(jì)算
聯(lián)合體所占的空間不僅取決于最寬成員,還跟所有成員有關(guān)系,即其大小必須滿足兩個(gè)條件:- 大小足夠容納最寬的成員;
- 大小能被其包含的所有基本數(shù)據(jù)類(lèi)型的大小所整除。
class?A?{};:?sizeof(A)?=?1;
class?A?{?virtual?Fun(){}?};:?sizeof(A)?=?4(32位機(jī)器)/8(64位機(jī)器);
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;
指針和引用
區(qū)別
- 定義:指針是一個(gè)對(duì)象,引用本身不是對(duì)象,只是另一個(gè)對(duì)象的別名;
- 指針是“指向”另外一種類(lèi)型的復(fù)合類(lèi)型;
- 引用本身不是一個(gè)對(duì)象,所以不能定義引用的引用;
- 引用只能綁定到對(duì)象上,它只是一個(gè)對(duì)象的別名,因此引用必須初始化,且不能更換引用對(duì)象。
指針
可以有 const 指針,但是沒(méi)有 const 引用(const 引用可讀不可改,與綁定對(duì)象是否為 const 無(wú)關(guān))注:引用可以指向常量,也可以指向變量。例如int