打工人的面試題集錦
前言: 經(jīng)常回顧C(jī)/C++面試題和筆試題,有助于我們對(duì)C/C++基礎(chǔ)有一個(gè)新的認(rèn)識(shí)和記憶。在實(shí)際工作中,大部分人會(huì)被業(yè)務(wù)纏身,基礎(chǔ)慢慢的遺忘了,對(duì)很多基礎(chǔ)知識(shí)欲言又止!回顧筆試面試題目,會(huì)讓我們對(duì)基礎(chǔ)知識(shí)掌握更加牢固,同時(shí)筆試題也有助于我們開闊思路,提高編程能力,還有就是開源代碼閱讀,只有不斷的回顧和學(xué)習(xí),才能不斷的進(jìn)步。
加油吧!打工人!~

(2)結(jié)構(gòu)體的總大小為結(jié)構(gòu)體的有效對(duì)齊值的整數(shù)倍(默認(rèn)以結(jié)構(gòu)體中最長的成員長度為有效值的整數(shù)倍,當(dāng)用#pragrma pack(n)指定時(shí),以n和結(jié)構(gòu)體中最長的成員的長度中較小者為其值)。即sizeof的值,必須是其內(nèi)部最大成員的整數(shù)倍,不足的要補(bǔ)齊。
例如:
class A
{
char c;
int a;
char d;
};
cout << sizeof(A) << endl;
class B
{
char c;
char d;
int a;
};
cout << sizeof(B) << endl;
sizeof(A)=12,sizeof(B)=8;
所以, 有時(shí)候在我們寫下如 AAA = XXX, 這樣的代碼, 且恰好XXX的類型正好是AAA單參數(shù)構(gòu)造器的參數(shù)類型, 這時(shí)候 編譯器就自動(dòng)調(diào)用這個(gè)構(gòu)造器, 創(chuàng)建一個(gè)AAA的對(duì)象。
這樣看起來好象很酷, 很方便。但在某些情況下(見下面權(quán)威的例子), 卻違背了我們(程序員)的本意。這時(shí)候就要在這個(gè)構(gòu)造器前面加上explicit修飾, 指定這個(gè)構(gòu)造器只能被明確的調(diào)用/使用, 不能作為類型轉(zhuǎn)換操作符被隱含的使用。
class Test1
{
public:
Test1(int n)
{
num=n;
}//普通構(gòu)造函數(shù)
private:
int num;
};
class Test2
{
public:
explicit Test2(int n)
{
num=n;
}//explicit(顯式)構(gòu)造函數(shù)
private:
int num;
};
int main()
{
Test1 t1=12;//隱式調(diào)用其構(gòu)造函數(shù),成功
Test2 t2=12;//編譯錯(cuò)誤,不能隱式調(diào)用其構(gòu)造函數(shù)
Test2 t2(12);//顯式調(diào)用成功
return 0;
}
普通構(gòu)造函數(shù)能夠被 隱式調(diào)用。而explicit構(gòu)造函數(shù)只能被顯式調(diào)用。
bool 型數(shù)據(jù):
if( flag )
{
A;
}
else
{
B;
}
int 型數(shù)據(jù):
if( 0 != flag )
{
A;
}
else {
B;
}
指針型數(shù):
if( NULL == flag )
{
A;
}
else {
B;
}
float 型數(shù)據(jù):
if ( ( flag >= NORM ) && ( flag <= NORM ) )
{
A;
}
代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復(fù)的對(duì)象實(shí)體
遞歸調(diào)用太深,導(dǎo)致堆棧溢出等
內(nèi)存泄漏最終導(dǎo)致內(nèi)存溢出
內(nèi)存泄漏是指向系統(tǒng)申請(qǐng)分配內(nèi)存進(jìn)行使用(new),但是用完后不歸還(delete),導(dǎo)致占用有效內(nèi)存。常見的幾種情況:
兩種情況下會(huì)出現(xiàn)這種內(nèi)存泄露:一是在堆里創(chuàng)建了對(duì)象占用了內(nèi)存,但是沒有顯示地釋放對(duì)象占用的內(nèi)存;二是在類的構(gòu)造函數(shù)中動(dòng)態(tài)的分配了內(nèi)存,但是在析構(gòu) 函數(shù)中沒有釋放內(nèi)存或者沒有正確的釋放內(nèi)存</br>
方括號(hào)是告訴編譯器這個(gè)指針指向的是一個(gè)對(duì)象數(shù)組,同時(shí)也告訴編譯器正確的對(duì)象地址值病調(diào)用對(duì)象的析構(gòu)函數(shù),如果沒有方括號(hào),那么這個(gè)指針就被默認(rèn)為只指向一個(gè)對(duì)象,對(duì)象數(shù)組中的其他對(duì)象的析構(gòu)函數(shù)就不會(huì)被調(diào)用,結(jié)果造成了內(nèi)存泄露。</br>
當(dāng)基類指針指向子類對(duì)象時(shí),如果基類的析構(gòu)函數(shù)不是virtual,那么子類的析構(gòu)函數(shù)將不會(huì)被調(diào)用,子類的資源沒有正確是釋放,因此造成內(nèi)存泄露</br>
參考鏈接:https://blog.csdn.net/hyqwmxsh/article/details/52813307
緩沖區(qū)溢出(棧溢出)</br>
程序?yàn)榱伺R時(shí)存取數(shù)據(jù)的需要,一般會(huì)分配一些內(nèi)存空間稱為緩沖區(qū)。如果向緩沖區(qū)中寫入緩沖區(qū)無法容納的數(shù)據(jù),機(jī)會(huì)造成緩沖區(qū)以外的存儲(chǔ)單元被改寫,稱為緩沖區(qū)溢出。而棧溢出是緩沖區(qū)溢出的一種,原理也是相同的。分為上溢出和下溢出。其中,上溢出是指棧滿而又向其增加新的數(shù)據(jù),導(dǎo)致數(shù)據(jù)溢出;下溢出是指空棧而又進(jìn)行刪除操作等,導(dǎo)致空間溢出。</br>
1 sizeof 是一個(gè)操作符,strlen 是庫函數(shù)。
2 sizeof 的參數(shù)可以是數(shù)據(jù)的類型,也可以是變量,而 strlen 只能以結(jié)尾為‘\0‘的字符串作參數(shù)。
3 編譯器在編譯時(shí)就計(jì)算出了 sizeof 的結(jié)果。而 strlen 函數(shù)必須在運(yùn)行時(shí)才能計(jì)算出來。并且 sizeof 計(jì)算的是數(shù)據(jù)類型占內(nèi)存的大小,而 strlen 計(jì)算的是字符串實(shí)際的長度。
4 數(shù)組做 sizeof 的參數(shù)不退化,傳遞給 strlen 就退化為指針了。
注意:有些是操作符看起來像是函數(shù),而有些函數(shù)名看起來又像操作符,這類容易混淆的名稱一定要加以區(qū)分,否則遇到數(shù)組名這類特殊數(shù)據(jù)類型作參數(shù)時(shí)就很容易出錯(cuò)。最容易混淆為函數(shù)的操作符就是 sizeof。
(1) new、delete 是操作符,可以重載,只能在 C++中使用。
(2) malloc、free 是函數(shù),可以覆蓋,C、C++中都可以使用。
(3) new 可以調(diào)用對(duì)象的構(gòu)造函數(shù),對(duì)應(yīng)的 delete 調(diào)用相應(yīng)的析構(gòu)函數(shù)。
(4) malloc 僅僅分配內(nèi)存,free 僅僅回收內(nèi)存,并不執(zhí)行構(gòu)造和析構(gòu)函數(shù)
(5) new、delete 返回的是某種數(shù)據(jù)類型指針,malloc、free 返回的是 void 指針。
注意:malloc 申請(qǐng)的內(nèi)存空間要用 free 釋放,而 new 申請(qǐng)的內(nèi)存空間要用 delete 釋放,不要混用。因?yàn)閮烧邔?shí)現(xiàn)的機(jī)理不同。
#define min(a,b)((a)<=(b)?(a):(b))
注意:在調(diào)用時(shí)一定要注意這個(gè)宏定義的副作用,如下調(diào)用:
((++*p)<=(x)?(++*p):(x)。
p 指針就自加了兩次,違背了 MIN 的本意。
可以,因?yàn)橹羔樅推胀ㄗ兞恳粯?,有時(shí)也有變化程序的不可控性。常見例:子中斷服務(wù)子程序修改一個(gè)指向一個(gè) buffer 的指針時(shí),必須用 volatile 來修飾這個(gè)指針。
說明:指針是一種普通的變量,從訪問上沒有什么不同于其他變量的特性。其保存的數(shù)值是個(gè)整型數(shù)據(jù),和整型變量不同的是,這個(gè)整型數(shù)據(jù)指向的是一段內(nèi)存地址。
請(qǐng)寫出以下代碼的打印結(jié)果,主要目的是考察 a 和&a 的區(qū)別。
#include<stdio.h>
void main( void )
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1)); return;
}
注意:數(shù)組名 a 可以作數(shù)組的首地址,而&a 是數(shù)組的指針。思考,將原式的 int *ptr=(int *)(&a+1); 改為 int *ptr=(int *)(a+1);時(shí)輸出結(jié)果將是什么呢?
C、C++中內(nèi)存分配方式可以分為三種:
(1) 從靜態(tài)存儲(chǔ)區(qū)域分配:
內(nèi)存在程序編譯時(shí)就已經(jīng)分配好,這塊內(nèi)存在程序的整個(gè)運(yùn)行期間都存在。速度快、不容易出錯(cuò),因?yàn)橛邢到y(tǒng)會(huì)善后。例如全局變量,static 變量等。
(2) 在棧上分配:
在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。
(3) 從堆上分配:
即動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的時(shí)候用 malloc 或 new 申請(qǐng)任意大小的內(nèi)存,程序員自己負(fù)責(zé)在何時(shí)用 free 或 delete 釋放內(nèi)存。動(dòng)態(tài)內(nèi)存的生存期由程序員決定,使用非常靈活。如果在堆上分配了空間,就有責(zé)任回收它,否則運(yùn)行的程序會(huì)出現(xiàn)內(nèi)存泄漏,另外頻繁地分配和釋放不同大小的堆空間將會(huì)產(chǎn)生堆內(nèi)碎塊。
一個(gè) C、C++程序編譯時(shí)內(nèi)存分為 5 大存儲(chǔ)區(qū):堆區(qū)、棧區(qū)、全局區(qū)、文字常量區(qū)、程序代碼區(qū)。
map和set的底層實(shí)現(xiàn)主要通過紅黑樹來實(shí)現(xiàn)
面向?qū)ο蟮娜筇卣魇欠庋b性、繼承性和多態(tài)性:
? 封裝性:將客觀事物抽象成類,每個(gè)類對(duì)自身的數(shù)據(jù)和方法實(shí)行 protection(private, protected, public)。
? 繼承性:廣義的繼承有三種實(shí)現(xiàn)形式:實(shí)現(xiàn)繼承(使用基類的屬性和方法而無需額外編碼的能力)、可視繼承(子窗體使用父窗體的外觀和實(shí)現(xiàn)代碼)、接口繼承(僅使用屬性和方法,實(shí)現(xiàn)滯后到子類實(shí)現(xiàn))。
? 多態(tài)性:是將父類對(duì)象設(shè)置成為和一個(gè)或更多它的子對(duì)象相等的技術(shù)。用子類對(duì)象給父類對(duì)象賦值之后,父類對(duì)象就可以根據(jù)當(dāng)前賦值給它的子對(duì)象的特性以不同的方式運(yùn)作。
說明:面向?qū)ο蟮娜齻€(gè)特征是實(shí)現(xiàn)面向?qū)ο蠹夹g(shù)的關(guān)鍵,每一個(gè)特征的相關(guān)技術(shù)都非常的復(fù)雜,程序員應(yīng)該多看、多練。
? 缺省構(gòu)造函數(shù)。
? 缺省拷貝構(gòu)造函數(shù)。
? 缺省析構(gòu)函數(shù)。
? 缺省賦值運(yùn)算符。
? 缺省取址運(yùn)算符。
? 缺省取址運(yùn)算符 const。
注意:有些書上只是簡單的介紹了前四個(gè)函數(shù)。沒有提及后面這兩個(gè)函數(shù)。但后面這兩個(gè)函數(shù)也是空類的默認(rèn)函數(shù)。另外需要注意的是,只有當(dāng)實(shí)際使用這些函數(shù)的時(shí)候,編譯器才會(huì)去定義它們。
拷貝構(gòu)造函數(shù)和賦值運(yùn)算符重載有以下兩個(gè)不同之處:
(1) 拷貝構(gòu)造函數(shù)生成新的類對(duì)象,而賦值運(yùn)算符不能。
(2) 由于拷貝構(gòu)造函數(shù)是直接構(gòu)造一個(gè)新的類對(duì)象,所以在初始化這個(gè)對(duì)象之前不用檢驗(yàn)源對(duì)象是否和新建對(duì)象相同。而賦值運(yùn)算符則需要這個(gè)操作,另外賦值運(yùn)算中如果原來的對(duì)象中有內(nèi)存分配要先把內(nèi)存釋放掉
注意:當(dāng)有類中有指針類型的成員變量時(shí),一定要重寫拷貝構(gòu)造函數(shù)和賦值運(yùn)算符,不要使用默認(rèn)的。
vector使用的注意點(diǎn)及其原因,頻繁對(duì)vector調(diào)用push_back()對(duì)性能的影響和原因。vector就是一個(gè)動(dòng)態(tài)增長的數(shù)組,里面有一個(gè)指針指向一片連續(xù)的空間,當(dāng)空間裝不下的時(shí)候,會(huì)申請(qǐng)一片更大的空間,將原來的數(shù)據(jù)拷貝過去,并釋放原來的舊空間。當(dāng)刪除的時(shí)候空間并不會(huì)被釋放,只是清空了里面的數(shù)據(jù)。對(duì)比array是靜態(tài)空間一旦配置了就不能改變大小。
template <typename T> class A
{
friend T; private:
A() {}
~A() {}
};
class B : virtual public A<B>
{ public:
B() {}
~B() {}
};
class C : virtual public B
{ public:
C() {}
~C() {}
};
void main( void )
{
B b; //C c;
return;
}
map是STL中的一個(gè)關(guān)聯(lián)容器,提供鍵值對(duì)的數(shù)據(jù)管理。底層通過紅黑樹來實(shí)現(xiàn),實(shí)際上是二叉排序樹和非嚴(yán)格意義上的二叉平衡樹。所以在map內(nèi)部所有的數(shù)據(jù)都是有序的,且map的查詢、插入、刪除操作的時(shí)間復(fù)雜度都是O(logN)。
(1)重寫和重載主要有以下幾點(diǎn)不同。
? 范圍的區(qū)別:被重寫的和重寫的函數(shù)在兩個(gè)類中,而重載和被重載的函數(shù)在同
? 參數(shù)的區(qū)別:被重寫函數(shù)和重寫函數(shù)的參數(shù)列表一定相同,而被重載函數(shù)和重載函數(shù)的參數(shù)列表一定不同。
? virtual 的區(qū)別:重寫的基類中被重寫的函數(shù)必須要有 virtual 修飾,而重載函數(shù)和被重載函數(shù)可以被 virtual 修飾,也可以沒有。
(2)隱藏和重寫、重載有以下幾點(diǎn)不同。
? 與重載的范圍不同:和重寫一樣,隱藏函數(shù)和被隱藏函數(shù)不在同一個(gè)類中。
? 參數(shù)的區(qū)別:隱藏函數(shù)和被隱藏的函數(shù)的參數(shù)列表可以相同,也可不同,但是函數(shù)名肯定要相同。當(dāng)參數(shù)不相同時(shí),無論基類中的參數(shù)是否被 virtual 修飾,基類的函數(shù)都是被隱藏,而不是被重寫。
說明:雖然重載和覆蓋都是實(shí)現(xiàn)多態(tài)的基礎(chǔ),但是兩者實(shí)現(xiàn)的技術(shù)完全不相同,達(dá)到的目的也是完全不同的,覆蓋是動(dòng)態(tài)態(tài)綁定的多態(tài),而重載是靜態(tài)綁定的多態(tài)。
編譯器發(fā)現(xiàn)一個(gè)類中有虛函數(shù),便會(huì)立即為此類生成虛函數(shù)表 vtable。虛函數(shù)表的各表項(xiàng)為指向?qū)?yīng)虛函數(shù)的指針。編譯器還會(huì)在此類中隱含插入一個(gè)指針 vptr(對(duì) vc 編譯器來說,它插在類的第一個(gè)位置上)指向虛函數(shù)表。調(diào)用此類的構(gòu)造函數(shù)時(shí),在類的構(gòu)造函數(shù)中,編譯器會(huì)隱含執(zhí)行 vptr 與 vtable 的關(guān)聯(lián)代碼,將 vptr 指向?qū)?yīng)的 vtable,將類與此類的 vtable 聯(lián)系了起來。另外在調(diào)用類的構(gòu)造函數(shù)時(shí),指向基礎(chǔ)類的指針此時(shí)已經(jīng)變成指向具體的類的 this 指針,這樣依靠此 this 指針即可得到正確的 vtable,。
如此才能真正與函數(shù)體進(jìn)行連接,這就是動(dòng)態(tài)聯(lián)編,實(shí)現(xiàn)多態(tài)的基本原理。
注意:一定要區(qū)分虛函數(shù),純虛函數(shù)、虛擬繼承的關(guān)系和區(qū)別。牢記虛函數(shù)實(shí)現(xiàn)原理,因?yàn)槎鄳B(tài) C++面試的重要考點(diǎn)之一,而虛函數(shù)是實(shí)現(xiàn)多態(tài)的基礎(chǔ)。
在C++中,內(nèi)存被分成五個(gè)區(qū):棧、堆、自由存儲(chǔ)區(qū)、靜態(tài)存儲(chǔ)區(qū)、常量區(qū)

隊(duì)列和棧都是線性存儲(chǔ)結(jié)構(gòu),但是兩者的插入和刪除數(shù)據(jù)的操作不同,隊(duì)列是“先進(jìn)先出”,棧是 “后進(jìn)先出”。
注意:區(qū)別棧區(qū)和堆區(qū)。堆區(qū)的存取是“順序隨意”,而棧區(qū)是“后進(jìn)先出”。棧由編譯器自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。堆一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由 OS 回收。分配方式類似于鏈表。
1、構(gòu)造函數(shù)不能聲明為虛函數(shù)
大端模式,是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的低地址。
小端模式,是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的高地址中

int a=1;
char *p=(char *)&a;
if(*p==1) printf("小端\n");
else printf("大端\n");
寄存器自增
寫回內(nèi)存
這三個(gè)階段中間都可以被中斷分離開.
(1)指針只是一個(gè)變量,只不過這個(gè)變量存儲(chǔ)的是一個(gè)地址;而引用跟原來的變量實(shí)質(zhì)上是同一個(gè)東西,只不過是原變量的一個(gè)別名而已,不占用內(nèi)存空間。(2)引用必須在定義的時(shí)候初始化,而且初始化后就不能再改變;而指針不必在定義的時(shí)候初始化,初始化后可以改變。(3)指針可以為空,但引用不能為空(這就意味著我們拿到一個(gè)引用的時(shí)候,是不需要判斷引用是否為空的,而拿到一個(gè)指針的時(shí)候,我們則需要判斷它是否為空。這點(diǎn)經(jīng)常在判斷函數(shù)參數(shù)是否有效的時(shí)候使用。) (4)“sizeof 引用" = 指向變量的大小 , "sizeof 指針"= 指針本身的大小 (5)指針可以有多級(jí),而引用只能是一級(jí)
(1)malloc與free是C++/C語言的標(biāo)準(zhǔn)庫函數(shù),new/delete是C++的運(yùn)算符。它們都可用于申請(qǐng)動(dòng)態(tài)內(nèi)存和釋放內(nèi)存。
(2)對(duì)于非內(nèi)部數(shù)據(jù)類型的對(duì)象而言,光用malloc/free無法滿足動(dòng)態(tài)對(duì)象的要求。對(duì)象在創(chuàng)建的同時(shí)要自動(dòng)執(zhí)行構(gòu)造函數(shù),對(duì)象在消亡之前要自動(dòng)執(zhí)行析構(gòu)函數(shù)。
(3)new可以認(rèn)為是malloc加構(gòu)造函數(shù)的執(zhí)行。new出來的指針是直接帶類型信息的。而malloc返回的都是void指針。
寫一個(gè)“標(biāo)準(zhǔn)”宏 MIN,這個(gè)宏輸入兩個(gè)參數(shù)并且返回較小的一個(gè)。
【答案】
#define min(a,b)((a)<=(b)?(a):(b))
注意:在調(diào)用時(shí)一定要注意這個(gè)宏定義的副作用,如下調(diào)用:
((++*p)<=(x)?(++*p):(x)。
p 指針就自加了兩次,違背了 MIN 的本意。
(1) 用法不同:typedef 用來定義一種數(shù)據(jù)類型的別名,增強(qiáng)程序的可讀性。define 主要用來定義常量,以及書寫復(fù)雜使用頻繁的宏。
(2) 執(zhí)行時(shí)間不同:typedef 是編譯過程的一部分,有類型檢查的功能。define 是宏定義,是預(yù)編譯的部分,其發(fā)生在編譯之前,只是簡單的進(jìn)行字符串的替換,不進(jìn)行類型的檢查。
(3) 作用域不同:typedef 有作用域限定。define 不受作用域約束,只要是在 define 聲明后的引用都是正確的。(4) 對(duì)指針的操作不同:typedef 和 define 定義的指針時(shí)有很大的區(qū)別。
注意:typedef 定義是語句,因?yàn)榫湮惨由戏痔?hào)。而 define 不是語句,千萬不能在句尾加分號(hào)。
(1)定義變量為只讀變量,不可修改
(2)修飾函數(shù)的參數(shù)和返回值(后者應(yīng)用比較少,一般為值傳遞)
(3)const成員函數(shù)(只需要在成員函數(shù)參數(shù)列表后加上關(guān)鍵字const,如char get() const;)可以訪問const成員變量和非const成員變量,但不能修改任何變量。在聲明一個(gè)成員函數(shù)時(shí),若該成員函數(shù)并不對(duì)數(shù)據(jù)成員進(jìn)行修改操作,應(yīng)盡可能將該成員函數(shù)聲明為const成員函數(shù)。
(4)const對(duì)象只能訪問const成員函數(shù),而非const對(duì)象可以訪問任意的成員函數(shù),包括const成員函數(shù).即對(duì)于class A,有const A a;那么a只能訪問A的const成員函數(shù)。而對(duì)于:A b;b可以訪問任何成員函數(shù)。
1)函數(shù)體內(nèi):static 修飾的局部變量作用范圍為該函數(shù)體,不同于auto變量,其內(nèi)存只被分配一次,因此其值在下次調(diào)用的時(shí)候維持了上次的值
extern置于變量或函數(shù)前,用于標(biāo)示變量或函數(shù)的定義在別的文件中,提示編譯器遇到此變量和函數(shù)時(shí)在其他模塊中尋找其定義。它只要有兩個(gè)作用:
(2)當(dāng)extern不與“C”在一起修飾變量或函數(shù)時(shí),如:extern int g_Int;它的作用就是聲明函數(shù)或全局變量的作用范圍的關(guān)鍵字,其聲明的函數(shù)和變量可以在本模塊或其他模塊中使用。記住它是一個(gè)聲明不是定義!也就是說B模塊(編譯單元)要是引用模塊(編譯單元)A中定義的全局變量或函數(shù)時(shí),它只要包含A模塊的頭文件即可,在編譯階段,模塊B雖然找不到該函數(shù)或變量,但它不會(huì)報(bào)錯(cuò),它會(huì)在連接時(shí)從模塊A生成的目標(biāo)代碼中找到此函數(shù)。
在程序中,流操作符>>和<<經(jīng)常連續(xù)使用。因此這兩個(gè)操作符的返回值應(yīng)該是一個(gè)仍舊支持這兩個(gè)操作符的流引用。其他的數(shù)據(jù)類型都無法做到這一點(diǎn)。
注意:除了在賦值操作符和流操作符之外的其他的一些操作符中,如+、-、*、/等卻千萬不能返回引用。因?yàn)檫@四個(gè)操作符的對(duì)象都是右值,因此,它們必須構(gòu)造一個(gè)對(duì)象作為返回值。
系統(tǒng)自動(dòng)生成的構(gòu)造函數(shù):普通構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù) (在沒有定義對(duì)應(yīng)的構(gòu)造函數(shù)的時(shí)候)
#include <iostream>
#include <string>
using namespace std;
class A{
private:
int data;
public:
A(int i){ data = i;} //自定義的構(gòu)造函數(shù)
A(A && a); //拷貝構(gòu)造函數(shù)
int getdata(){return data;}
};
//拷貝構(gòu)造函數(shù)
A::A(A && a){
data = a.data;
cout <<"拷貝構(gòu)造函數(shù)執(zhí)行完畢"<<endl;
}
//參數(shù)是對(duì)象,值傳遞,調(diào)用拷貝構(gòu)造函數(shù)
int getdata1(A a){
return a.getdata();
}
//參數(shù)是引用,引用傳遞,不調(diào)用拷貝構(gòu)造函數(shù)
int getdata2(A &a){
return a.getdata();
}
//返回值是對(duì)象類型,會(huì)調(diào)用拷貝構(gòu)造函數(shù)
A getA1(){
A a(0);
return a;
}
//返回值是引用類型,會(huì)調(diào)用拷貝構(gòu)造函數(shù),因?yàn)楹瘮?shù)體內(nèi)生成的對(duì)象是臨時(shí)的,離開函數(shù)就消失
A& getA2(){
A a(0);
return a;
}
int main(){
A a1(1);
A b1(a1); //用a1初始化b1,調(diào)用拷貝構(gòu)造函數(shù)
A c1=a1; //用a1初始化c1,調(diào)用拷貝構(gòu)造函數(shù)
int i=getdata1(a1); //函數(shù)形參是類的對(duì)象,調(diào)用拷貝構(gòu)造函數(shù)
int j=getdata2(a1); //函數(shù)形參類型是引用,不調(diào)用拷貝構(gòu)造函數(shù)
A d1=getA1(); //調(diào)用拷貝構(gòu)造函數(shù)
A e1=getA2(); //調(diào)用拷貝構(gòu)造函數(shù)
return 0;
}
類型安全很大程度上可以等價(jià)于內(nèi)存安全,類型安全的代碼不會(huì)試圖訪問自己沒被授權(quán)的內(nèi)存區(qū)域。C只在局部上下文中表現(xiàn)出類型安全,比如試圖從一種結(jié)構(gòu)體的指針轉(zhuǎn)換成另一種結(jié)構(gòu)體的指針時(shí),編譯器將會(huì)報(bào)告錯(cuò)誤,除非使用顯式類型轉(zhuǎn)換。然而,C中相當(dāng)多的操作是不安全的。
dynamic_cast<T*>(content) 動(dòng)態(tài)類型轉(zhuǎn)換;也是向下安全轉(zhuǎn)型;是在運(yùn)行的時(shí)候執(zhí)行;基類中一定要有虛函數(shù),否則編譯不通過。在類層次間進(jìn)行上行轉(zhuǎn)換時(shí)(如派生類指針轉(zhuǎn)為基類指針),dynamic_cast和static_cast的效果是一樣的。在進(jìn)行下行轉(zhuǎn)換時(shí)(如基類指針轉(zhuǎn)為派生類指針),dynamic_cast具有類型檢查的功能,比static_cast更安全。
const_cast<T*>(content) 去常轉(zhuǎn)換;編譯時(shí)執(zhí)行;
reinterpret_cast<T*>(content) 重解釋類型轉(zhuǎn)換;
“野指針”產(chǎn)生原因及解決辦法如下:
(1) 指針變量聲明時(shí)沒有被初始化。解決辦法:指針聲明時(shí)初始化,可以是具體的地址值,也可讓它指向 NULL。
(2) 指針 p 被 free 或者 delete 之后,沒有置為 NULL。解決辦法:指針指向的內(nèi)存空間被釋放后指針應(yīng)該指向 NULL。
(3) 指針操作超越了變量的作用范圍。解決辦法:在變量的作用域結(jié)束前釋放掉變量的地址空間并且讓指針指向 NULL。注意:“野指針”的解決方法也是編程規(guī)范的基本原則,平時(shí)使用指針時(shí)一定要避免產(chǎn)生“野指針”,在使用指針前一定要檢驗(yàn)指針的合法性。
#define是預(yù)處理命令,在預(yù)處理是執(zhí)行簡單的替換,不做正確性的檢查
typedef (int*) pINT;
(1)構(gòu)造函數(shù)中計(jì)數(shù)初始化為1;
(2)拷貝構(gòu)造函數(shù)中計(jì)數(shù)值加1;
(3)賦值運(yùn)算符中,左邊的對(duì)象引用計(jì)數(shù)減一,右邊的對(duì)象引用計(jì)數(shù)加一;
(4)析構(gòu)函數(shù)中引用計(jì)數(shù)減一;
(5)在賦值運(yùn)算符和析構(gòu)函數(shù)中,如果減一后為0,則調(diào)用delete釋放對(duì)象。
1)函數(shù)調(diào)用層次過深,每調(diào)用一次,函數(shù)的參數(shù)、局部變量等信息就壓一次棧
構(gòu)造函數(shù)不能是虛函數(shù)。而且不能在構(gòu)造函數(shù)中調(diào)用虛函數(shù),因?yàn)槟菢訉?shí)際執(zhí)行的是父類的對(duì)應(yīng)函數(shù),因?yàn)樽约哼€沒有構(gòu)造好。析構(gòu)函數(shù)可以是虛函數(shù),而且,在一個(gè)復(fù)雜類結(jié)構(gòu)中,這往往是必須的。
析構(gòu)函數(shù)也可以是純虛函數(shù),但純虛析構(gòu)函數(shù)必須有定義體,因?yàn)槲鰳?gòu)函數(shù)的調(diào)用是在子類中隱含的。
說明:虛函數(shù)的動(dòng)態(tài)綁定特性是實(shí)現(xiàn)重載的關(guān)鍵技術(shù),動(dòng)態(tài)綁定根據(jù)實(shí)際的調(diào)用情況查詢相應(yīng)類的虛函數(shù)表,調(diào)用相應(yīng)的虛函數(shù)。
所謂的面向?qū)ο缶褪菍⑽覀兊某绦蚰K化,對(duì)象化,把具體事物的特性屬性和通過這些屬性來實(shí)現(xiàn)一些動(dòng)作的具體方法放到一個(gè)類里面,這就是封裝。封裝是我們所說的面相對(duì)象編程的特征之一。除此之外還有繼承和多態(tài)。繼承有點(diǎn)類似與我們生物學(xué)上的遺傳,就是子類的一些特征是來源于父類的,兒子遺傳了父親或母親的一些性格,或者相貌,又或者是運(yùn)動(dòng)天賦。有點(diǎn)種瓜得瓜種豆得豆的意思。面向?qū)ο罄锏睦^承也就是父類的相關(guān)的屬性,可以被子類重復(fù)使用,子類不必再在自己的類里面重新定義一回,父類里有點(diǎn)我們只要拿過來用就好了。而對(duì)于自己類里面需要用到的新的屬性和方法,子類就可以自己來擴(kuò)展了。當(dāng)然,會(huì)出現(xiàn)一些特殊情況,就是我們?cè)谟幸恍┓椒ㄔ诟割愐呀?jīng)定義好了,但是子類我們自己再用的時(shí)候,發(fā)現(xiàn),其實(shí),我們的雖然都是計(jì)算工資的,但是普通員工的工資計(jì)算方法跟經(jīng)理的計(jì)算方法是不一樣的,所以這個(gè)時(shí)候,我們就不能直接調(diào)用父類的這個(gè)計(jì)算工資的方法了。這個(gè)時(shí)候我們就需要用到面向?qū)ο蟮牧硪粋€(gè)特性,多態(tài)。對(duì),就是多態(tài),我們要在子類里面把父類里面定義計(jì)算工資的方法在子類里面重新實(shí)現(xiàn)一遍。多態(tài)包含了重載和重寫。重寫很簡單就是把子類從父親類里繼承下來的方法重新寫一遍,這樣,父類里相同的方法就被覆蓋了,當(dāng)然啦,你還是可以通過super.CaculSalary方法來調(diào)用父類的工資計(jì)算方法。而重載就是類里面相同方法名,不同形參的情況,可以是形參類型不同或者形參個(gè)數(shù)不同,或者形參順序不同,但是不能使返回值類型不同。
對(duì)比值傳遞,引用傳參的好處:
評(píng)論
圖片
表情

