1. 聲明與定義的區(qū)別
變量定義:為變量分配存儲空間端姚,可以為變量指定初始值
變量聲明:向程序表明存在一個(gè)變量
定義也是聲明,加上了extern的聲明不是定義讹俊,表明變量定義在其他地方
在一個(gè)程序中垦沉,變量只能定義一次,可以聲明多次
函數(shù)定義:帶有{}的是定義
函數(shù)聲明:沒有{}的是聲明
int a; // 定義
int b = 0; // 定義劣像,初始化
extern int c; // 聲明乡话,非定義
int double funca(int a, int b); // 聲明函數(shù)
int double funcb(int a, int b){return max(a, b);} // 定義函數(shù)
extern double funcc(int a, int b); // 聲明函數(shù)
2. extern和static的區(qū)別
extern用于聲明變量或函數(shù),并且表明這個(gè)變量或函數(shù)的定義在其他地方
static用于定義靜態(tài)變量或函數(shù)耳奕,并且該變量或函數(shù)只對當(dāng)前源文件可見
3. static的作用
當(dāng)使用static定義一個(gè)全局變量或函數(shù)時(shí)绑青,這個(gè)全局變量或函數(shù)成為靜態(tài)全局變量或靜態(tài)全局函數(shù)诬像,它只對當(dāng)前源文件可見,會對其他源文件隱藏
當(dāng)使用static定義一個(gè)局部變量時(shí)闸婴,這個(gè)局部變量成為靜態(tài)局部變量坏挠,它的存儲區(qū)域不在棧上而在靜態(tài)數(shù)據(jù)區(qū),當(dāng)這個(gè)局部執(zhí)行結(jié)束時(shí)邪乍,靜態(tài)局部變量的存儲區(qū)域不會被釋放降狠,當(dāng)?shù)诙卧L問到這個(gè)靜態(tài)局部變量時(shí),它的值不會變化
當(dāng)使用static定義類中的成員變量或函數(shù)時(shí)庇楞,這個(gè)成員變量或函數(shù)會成為整個(gè)類的成員榜配,而不屬于某一個(gè)具體的對象
static會對變量進(jìn)行自動初始化
4. x = x + 1, x += 1, x++的效率
第一個(gè)效率最低,因?yàn)槭紫纫x取右邊x的地址吕晌,然后加一操作蛋褥,然后讀取左邊x的地址,然后傳值給它
第二個(gè)效率第二低睛驳,它首先讀取x的地址烙心,然后加一操作,然后直接傳值給x
第三個(gè)效率最高乏沸,它首先讀取x的地址淫茵,然后直接x自增加一
5. const和宏定義的區(qū)別
const和define都可以用來定義一個(gè)常量
define在預(yù)處理階段對程序代碼進(jìn)行字面上的替換,沒有數(shù)據(jù)類型蹬跃,不會進(jìn)行類型檢查匙瘪;const定義一個(gè)只讀變量,有數(shù)據(jù)類型炬转,有類型檢查
由于define是做字面替換辆苔,有多少地方使用就替換多少次,宏常量在內(nèi)存中有多個(gè)備份扼劈;const定義的變量存儲在靜態(tài)數(shù)據(jù)區(qū)驻啤,使用的時(shí)候去靜態(tài)數(shù)據(jù)區(qū)調(diào)用,只有一個(gè)備份
6. strcpy和memcpy的區(qū)別
strcpy只用于字符串的復(fù)制荐吵,memcpy可以復(fù)制任意類型
strcpy在復(fù)制時(shí)不需要指定長度骑冗,遇到字符串結(jié)束符就結(jié)束,可能發(fā)生溢出先煎,memcpy需要指定復(fù)制的長度
char* strcpy(char* dest, const char* src){
if((src == NULL) || (dest == NULL)) // 判斷是否有效
return NULL;
strdest = dest; // 保存dest首地址
while(*src != 0){
*dest = *src;
dest++;
src++;
} // 逐個(gè)復(fù)制
return strdest;
void* memcpy(void* dest, const void* src, size_t size){
if((src == NULL) || (dest == NULL)) // 判斷是否有效
return NULL;
memdest = dest;
while(size--){
*dest = *src;
dest++;
src++;
}
return memdest;
}
7. new和malloc的區(qū)別
new是c++的關(guān)鍵字贼涩,malloc是c的庫函數(shù)
new對應(yīng)的是delete,malloc對應(yīng)的是free
在動態(tài)分配內(nèi)存的時(shí)候薯蝎,new不需要指定具體分配內(nèi)存的大小遥倦,編譯器會自動根據(jù)類型計(jì)算,malloc需要顯式指出分配內(nèi)存大小
new返回的是對應(yīng)類型的指針占锯,malloc返回的是void*
new分配失敗會拋出異常袒哥,malloc分配失敗返回NULL
new可以自動初始化缩筛,malloc需要用戶另外初始化
8. 構(gòu)造函數(shù)和析構(gòu)函數(shù)可不可以是虛函數(shù)
構(gòu)造函數(shù)不能是虛函數(shù)
虛函數(shù)調(diào)用需要用到虛函數(shù)指針,虛函數(shù)指針存放在對象的內(nèi)存空間中堡称,如果將構(gòu)造函數(shù)聲明為虛函數(shù)瞎抛,則對象還沒創(chuàng)建時(shí),沒有虛函數(shù)指針却紧,無法調(diào)用虛函數(shù)桐臊,無法調(diào)用構(gòu)造函數(shù)
析構(gòu)函數(shù)可以是虛函數(shù)
當(dāng)存在基類和派生類的時(shí)候,最好將析構(gòu)函數(shù)聲明為虛函數(shù)晓殊。如果有一個(gè)基類指針指向一個(gè)派生類對象断凶,并且析構(gòu)函數(shù)不是虛函數(shù),當(dāng)delete基類指針時(shí)巫俺,就會調(diào)用基類的析構(gòu)函數(shù)懒浮,派生類的新部分不會被釋放;如果將析構(gòu)函數(shù)聲明為虛函數(shù)识藤,當(dāng)delete基類指針時(shí),就會調(diào)用派生類的析構(gòu)函數(shù)次伶,釋放派生類對象的所有空間
9. 如何限制一個(gè)類只能在堆/棧上分配空間
只能在堆上分配空間:只能用new分配痴昧,不能直接調(diào)用類的構(gòu)造函數(shù)
將構(gòu)造函數(shù)和析構(gòu)函數(shù)設(shè)置為protected,然后寫新的public的函數(shù)來構(gòu)造和析構(gòu)冠王,用來構(gòu)造的函數(shù)通過new來構(gòu)造對象赶撰,用來析構(gòu)的函數(shù)通過delete來銷毀對象
class A{
protected:
A(){}
~A(){}
public:
static A* create(){
return new A();
}
void destroy(){
delete this;
}
};
只能在棧上分配空間:不能用new分配,將operator new()設(shè)置成私有
class A{
public:
A(){}
~A(){}
private:
A* operator new(size_t t){}
void operator delete(void* ptr){}
};
10. 拷貝構(gòu)造函數(shù)能夠使用值傳遞
不能柱彻。如果使用值傳遞豪娜,在調(diào)用拷貝構(gòu)造函數(shù)的時(shí)候,形參需要調(diào)用拷貝構(gòu)造函數(shù)給實(shí)參傳值哟楷,這樣就會無限調(diào)用拷貝構(gòu)造函數(shù)瘤载,無法完成拷貝
11. C++的內(nèi)存分配
程序占用內(nèi)存分為以下幾個(gè)部分:
棧區(qū):由編譯器自動分配釋放,存放運(yùn)行時(shí)函數(shù)中的參數(shù)卖擅、局部變量鸣奔、返回值、返回地址等惩阶,操作類似數(shù)據(jù)結(jié)構(gòu)中的棧挎狸,后進(jìn)先出
堆區(qū):由用戶通過new、malloc分配和delete断楷、free釋放锨匆,分配類似鏈表,可能會產(chǎn)生碎片冬筒,如果在堆上分配了空間忘了釋放恐锣,會造成內(nèi)存泄漏
全局區(qū):存放全局變量茅主、靜態(tài)數(shù)據(jù)、const常量侥蒙,分為未初始化區(qū)bss和已初始化區(qū)data
文字常量區(qū):存放常量字符串
代碼區(qū):存放代碼
12. 棧和堆的區(qū)別
管理方式不同:棧由編譯器自動分配和釋放暗膜,堆是程序員手動申請和釋放
空間大小不同:棧比堆小很多
生長方向不同:棧往下長,朝著內(nèi)存地址減小的方向鞭衩,堆往上長学搜,朝著內(nèi)存地址增大的方向
是否產(chǎn)生碎片:棧后進(jìn)先出,彈出一個(gè)元素前论衍,上一個(gè)元素已經(jīng)彈出瑞佩,不會產(chǎn)生碎片
分配效率不同:棧的分配效率比堆高,因?yàn)闂S袑iT的結(jié)構(gòu)存放棧頂?shù)刂放魈ǎ瑝簵3鰲1容^快炬丸,堆的分配要搜索能夠使用的內(nèi)存,比較慢
13. 動態(tài)內(nèi)存分配
有兩種數(shù)據(jù)結(jié)構(gòu)存放內(nèi)存使用情況:
空閑分區(qū)表:每個(gè)空閑分區(qū)對應(yīng)一個(gè)表項(xiàng)蜒蕾,含有分區(qū)號稠炬、分區(qū)大小、分區(qū)起始地址等信息
空閑分區(qū)鏈:每個(gè)分區(qū)的起始和末尾有指向上一個(gè)和下一個(gè)空閑分區(qū)指針咪啡,起始部分還存有分區(qū)大小等信息
動態(tài)分配算法:
首次適應(yīng):從頭到尾找到一個(gè)適合的分區(qū)首启,查找耗時(shí),實(shí)現(xiàn)簡單
最佳適應(yīng):優(yōu)先使用更小的分區(qū)撤摸,保留更多的大分區(qū)塊毅桃,滿足大進(jìn)程需求
最壞適應(yīng):優(yōu)先使用更大的分區(qū),減少難以利用的小碎片
鄰近適應(yīng):從當(dāng)前位置查找適合的分區(qū)准夷,查找簡單钥飞,實(shí)現(xiàn)簡單
伙伴內(nèi)存管理:
將空閑分區(qū)塊根據(jù)大小分組,2^i大小的分為一組衫嵌,每組形成一個(gè)鏈表读宙,分配時(shí)直接到相應(yīng)的鏈表查找有無空閑塊
如果沒有,就到更大的空閑塊鏈表查找渐扮,找到后將其拆分论悴,剩余的空閑塊插入到相應(yīng)大小的鏈表
釋放的時(shí)候,如果相鄰的位置有空閑塊墓律,就合并后插入到更大的內(nèi)存塊鏈表
14. struct的字節(jié)對齊
成員變量的起始地址為其長度的整數(shù)倍膀估,結(jié)構(gòu)體的總大小為其最大長度成員變量大小的整數(shù)倍
定義變量的順序不一樣,整個(gè)結(jié)構(gòu)體的大小會不一樣
struct A{
char a;
short b;
int c;
};
sizeof(A) = 8
struct B{
char a;
int b;
short c;
};
sizeof(B) = 12
15. 智能指針
智能指針能夠自動釋放它所指向的對象
有三種智能指針:unique_ptr耻讽、shared_ptr察纯、weak_ptr
unique pointer獨(dú)占它所指向的對象,shared pointer允許多個(gè)指針指向同一個(gè)對象,weak pointer是弱引用饼记,指向shared pointer管理的對象香伴,用來解決shared pointer中會出現(xiàn)的死鎖現(xiàn)象
每一個(gè)shared pointer都有一個(gè)關(guān)聯(lián)的計(jì)數(shù)器,統(tǒng)計(jì)它所指向?qū)ο蟮囊糜?jì)數(shù)具则,當(dāng)引用計(jì)數(shù)變?yōu)榱愕臅r(shí)候即纲,就會釋放所管理的對象內(nèi)存
weak pointer不會影響shared pointer指向?qū)ο蟮囊糜?jì)數(shù),當(dāng)引用計(jì)數(shù)為零的時(shí)候博肋,不管有沒有weak pointer指向?qū)ο蟮驼瑢ο蟮膬?nèi)存都會被釋放
class A;
class B;
class A{
public:
shared_ptr<B> pb;
};
class B{
public:
shared_ptr<A> pa;
};
void func(){
shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());
pa->pb = pb;
pb->pa = pa;
}
當(dāng)函數(shù)func執(zhí)行完畢跳出,pa匪凡、pb被釋放膊畴,pa、pb指向?qū)ο蟮囊糜?jì)數(shù)都減一變?yōu)?病游,所以A和B的具體對象并沒有被釋放唇跨,造成內(nèi)存泄漏
class A;
class B;
class A{
public:
weak_ptr<B> pb;
};
class B{
public:
shared_ptr<A> pa;
};
void func(){
shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());
pa->pb = pb;
pb->pa = pa;
}
當(dāng)函數(shù)體執(zhí)行完畢前,A的引用計(jì)數(shù)是2衬衬,B是1买猖,pa、pb被釋放滋尉,A和B的引用計(jì)數(shù)都減一政勃,B變?yōu)?,會被釋放兼砖,B中的pa指針被釋放,A對象的引用計(jì)數(shù)減一變?yōu)?既棺,A被釋放
16. 封裝讽挟、繼承、多態(tài)
是C++面向?qū)ο缶幊痰奶攸c(diǎn)
封裝是將數(shù)據(jù)和操作數(shù)據(jù)的方法封裝在一個(gè)類中丸冕,在類的內(nèi)部實(shí)現(xiàn)對數(shù)據(jù)的操作
將復(fù)雜的操作實(shí)現(xiàn)放在類的內(nèi)部耽梅,為外部提供接口,使得程序更容易被理解和分工
類的內(nèi)部成員有不同的權(quán)限胖烛,能防止程序中無關(guān)部分修改成員眼姐,數(shù)據(jù)更加安全
繼承是指派生類繼承基類,派生類擴(kuò)展基類的成員和方法佩番,使得代碼得到更好的復(fù)用
多態(tài)指一個(gè)函數(shù)存在多種實(shí)現(xiàn)方法
有兩種众旗,編譯時(shí)多態(tài)和運(yùn)行時(shí)多態(tài)
編譯時(shí)多態(tài)指的是重載,就是一個(gè)類中的函數(shù)有相同的函數(shù)名趟畏,但是參數(shù)列表不同
運(yùn)行時(shí)多態(tài)指的是重寫贡歧,通過虛函數(shù)實(shí)現(xiàn),在基類和派生類中有函數(shù)名稱和參數(shù)列表都相同的函數(shù),但是函數(shù)名前有virtual的關(guān)鍵字
17. 四種類型轉(zhuǎn)換
static cast:用于風(fēng)險(xiǎn)小的類型轉(zhuǎn)換利朵,比如浮點(diǎn)數(shù)和整數(shù)的轉(zhuǎn)換律想,派生類指針到基類指針的轉(zhuǎn)換
const cast:用于去除常量屬性
reinterpret cast:用于不同類型的指針之間的轉(zhuǎn)換,以及能容納得下指針的整數(shù)和指針之間的轉(zhuǎn)換
dynamic cast:用于含有虛函數(shù)的類的向基類或向派生類的轉(zhuǎn)換
18. 指針和引用的區(qū)別
指針存儲一個(gè)內(nèi)存地址绍弟,引用相當(dāng)于變量的別名
系統(tǒng)會為指針分配內(nèi)存技即,不會給引用分配內(nèi)存
指針可以修改所存的地址,引用一和變量綁定就無法再綁定其他變量
引用必須初始化
19. 指針和數(shù)組的區(qū)別
指針保存地址樟遣,數(shù)組保存相同數(shù)據(jù)類型的數(shù)據(jù)
指針通過地址間接訪問數(shù)據(jù)而叼,數(shù)組直接訪問數(shù)據(jù)
指針需要顯式分配和釋放內(nèi)存,數(shù)組自動分配和釋放
指針動態(tài)分配內(nèi)存大小年碘,數(shù)組在定義時(shí)就確定大小
20. 野指針是什么
指向非法內(nèi)存地址的指針
非法內(nèi)存:未分配的區(qū)域
21. 為什么不默認(rèn)析構(gòu)函數(shù)為虛函數(shù)
因?yàn)楹刑摵瘮?shù)的類需要虛函數(shù)表澈歉,其對象需要虛函數(shù)指針,將不含虛函數(shù)的類的析構(gòu)函數(shù)聲明為虛函數(shù)需要額外的空間開銷
22. 重載和重寫的區(qū)別
重載:同一個(gè)類中屿衅,相同函數(shù)名的函數(shù)埃难,參數(shù)列表不相同,在調(diào)用的時(shí)候根據(jù)參數(shù)列表選擇調(diào)用函數(shù)
重寫:基類和派生類中有函數(shù)名稱和參數(shù)列表都相同的函數(shù)涤久,函數(shù)聲明為virtual涡尘,在運(yùn)行的時(shí)候,根據(jù)具體的對象類型調(diào)用函數(shù)
23. 虛函數(shù)如何實(shí)現(xiàn)
通過虛函數(shù)表和虛函數(shù)指針來實(shí)現(xiàn)
每個(gè)類都有一個(gè)虛函數(shù)表响迂,表中存放虛函數(shù)的入口地址
每個(gè)對象有一個(gè)虛函數(shù)指針考抄,指向虛函數(shù)表
派生類會繼承基類的虛函數(shù)表,當(dāng)派生類重寫了基類的方法時(shí)蔗彤,派生類的虛函數(shù)表中相應(yīng)的函數(shù)的入口地址會更改
24. 隱式類型轉(zhuǎn)換
一種是用低精度變量給高精度變量賦值
一種是在含有單參數(shù)的構(gòu)造函數(shù)的類川梅,用相應(yīng)的單參數(shù)給類的對象賦值
25. 函數(shù)指針
指向函數(shù)入口地址的指針
可以用來調(diào)用函數(shù)或者作為函數(shù)的參數(shù)
26. struct和class的區(qū)別
默認(rèn)訪問權(quán)限不同,struct是public然遏,class是private
class可以用于聲明模板贫途,struct不可以
27. 內(nèi)存泄漏和場景
內(nèi)存泄漏指的是不再使用的內(nèi)存沒被釋放
常見的發(fā)生場景
new分配后沒有delete
沒有把基類的析構(gòu)函數(shù)聲明為虛函數(shù),一個(gè)基類指針指向派生類對象待侵,delete基類指針時(shí)沒有準(zhǔn)確的釋放派生類對象擴(kuò)充的部分
28. inline函數(shù)和宏定義
inline在編譯時(shí)將代碼嵌入相應(yīng)位置丢早,宏定義預(yù)處理階段簡單替換
inline函數(shù)有語法詞法分析,可以查出語法詞法錯(cuò)誤秧倾,宏定義沒有
inline函數(shù)不會有歧義怨酝,宏定義可能產(chǎn)生歧義