18 請解析(*(void (*)( ) )0)( )的含義
void (*0)( ) :是一個傳回值為void,參數為空的函數指針0。
(void (*)( ))0:把0轉變成一個傳回值為void,參數為空的函數指針。
*(void (*)( ))0:在上句的基礎上加*表示整個是一個傳回值為void,無參數,并且起始位址為0的函數的名字。
(*(void (*)( ))0)( ):這就是上句的函數名所對應的函數的調用。
19 C語言的指針和引用和c++的有什麼差別?
指針有自己的一塊空間,而引用隻是一個别名;
使用sizeof看一個指針的大小是4,而引用則是被引用對象的大小;
作為參數傳遞時,指針需要被解引用才可以對對象進行操作,而直接對引 用的修改都會改變引用所指向的對象;
可以有const指針,但是沒有const引用;
指針在使用中可以指向其它對象,但是引用隻能是一個對象的引用,不能 被改變;
指針可以有多級指針(**p),而引用止于一級;
指針和引用使用++運算符的意義不一樣;
如果傳回動态記憶體配置設定的對象或者記憶體,必須使用指針,引用可能引起記憶體洩露。
20 typedef 和define 有什麼差別
用法不同:typedef 用來定義一種資料類型的别名,增強程式的可讀性。define 主要用來定義 常量,以及書寫複雜使用頻繁的宏。
執行時間不同:typedef 是編譯過程的一部分,有類型檢查的功能。define 是宏定義,是預編譯的部分,其發生在編譯之前,隻是簡單的進行字元串的替換,不進行類型的檢查。
作用域不同:typedef 有作用域限定。define 不受作用域限制,隻要是在define 聲明後的引用 都是正确的。
對指針的操作不同:typedef 和define 定義的指針時有很大的差別。
注意:typedef 定義是語句,因為句尾要加上分号。而define 不是語句,千萬不能在句尾加分号。
21 指針常量與常量指針差別
指針常量是指定義了一個指針,這個指針的值隻能在定義時初始化,其他地方不能改變。常量指針 是指定義了一個指針,這個指針指向一個隻讀的對象,不能通過常量指針來改變這個對象的值。 指針常量強調的是指針的不可改變性,而常量指針強調的是指針對其所指對象的不可改變性。
注意:無論是指針常量還是常量指針,其最大的用途就是作為函數的形式參數,保證明參在被調用 函數中的不可改變特性。
22 簡述隊列和棧的異同
隊列和棧都是線性存儲結構,但是兩者的插入和删除資料的操作不同,隊列是“先進先出”,棧是 “後進先出”。
注意:差別棧區和堆區。堆區的存取是“順序随意”,而棧區是“後進先出”。棧由編譯器自動分 配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似于資料結構中的棧。堆一般由程式員 配置設定釋放, 若程式員不釋放,程式結束時可能由OS 回收。配置設定方式類似于連結清單。 它與本題中的堆和棧是兩回事。堆棧隻是一種資料結構,而堆區和棧區是程式的不同記憶體存儲區域。
23 設定位址為0x67a9 的整型變量的值為0xaa66
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa66;
注意:這道題就是強制類型轉換的典型例子,無論在什麼平台位址長度和整型資料的長度是一樣的, 即一個整型資料可以強制轉換成位址指針類型,隻要有意義即可。
24 編碼實作字元串轉化為數字
編碼實作函數atoi(),設計一個程式,把一個字元串轉化為一個整型數值。例如數字:“5486321 ”, 轉化成字元:5486321。
int myAtoi(const char * str)
{
int num = 0; //儲存轉換後的數值
int isNegative = 0; //記錄字元串中是否有負号
int n =0;
char *p = str;
if(p == NULL) //判斷指針的合法性
{
return -1;
}
while(*p++ != '\0') //計算數字元串度
{
n++;
}
p = str;
if(p[0] == '-') //判斷數組是否有負号
{
isNegative = 1;
}
char temp = '0';
for(int i = 0 ; i < n; i++)
{
char temp = *p++;
if(temp > '9' ||temp < '0') //濾除非數字字元
{
continue;
}
if(num !=0 || temp != '0') //濾除字元串開始的0 字元
{
temp -= 0x30; //将數字字元轉換為數值
num += temp *int( pow(10 , n - 1 -i) );
}
}
if(isNegative) //如果字元串中有負号,将數值取反
{
return (0 - num);
}
else
{
return num; //傳回轉換後的數值
}
}
25 C語言的結構體和C++的有什麼差別
C語言的結構體是不能有函數成員的,而C++的類可以有。
C語言的結構體中資料成員是沒有private、public和protected通路限定的。而C++的類的成員有這些通路限定。
C語言的結構體是沒有繼承關系的,而C++的類卻有豐富的繼承關系。
注意:雖然C的結構體和C++的類有很大的相似度,但是類是實作面向對象的基礎。而結構體隻可以簡單地了解為類的前身。
26 簡述指針常量與常量指針的差別
指針常量是指定義了一個指針,這個指針的值隻能在定義時初始化,其他地方不能改變。常量指針是指定義了一個指針,這個指針指向一個隻讀的對象,不能通過常量指針來改變這個對象的值。
指針常量強調的是指針的不可改變性,而常量指針強調的是指針對其所指對象的不可改變性。
注意:無論是指針常量還是常量指針,其最大的用途就是作為函數的形式參數,保證明參在被調用函數中的不可改變特性。
27 如何避免“野指針”
指針變量聲明時沒有被初始化。解決辦法:指針聲明時初始化,可以是具體的位址值,也可讓它指向NULL。
指針p被free或者delete之後,沒有置為NULL。解決辦法:指針指向的記憶體空間被釋放後指針應該指向NULL。
指針操作超越了變量的作用範圍。解決辦法:在變量的作用域結束前釋放掉變量的位址空間并且讓指針指向NULL。
28 句柄和指針的差別和聯系是什麼?
句柄和指針其實是兩個截然不同的概念。Windows系統用句柄标記系統資源,隐藏系統的資訊。你隻要知道有這個東西,然後去調用就行了,它是個32it的uint。指針則标記某個實體記憶體位址,兩者是不同的概念。
29 new/delete與malloc/free的差別是什麼
new能自動計算需要配置設定的記憶體空間,而malloc需要手工計算位元組數。
int *p = new int[2];
int *q = (int *)malloc(2*sizeof(int));
new與delete直接帶具體類型的指針,malloc和free傳回void類型的指針。
new類型是安全的,而malloc不是。例如int *p = new float[2];就會報錯;而int p = malloc(2sizeof(int))編譯時編譯器就無法指出錯誤來。
new一般分為兩步:new操作和構造。new操作對應與malloc,但new操作可以重載,可以自定義記憶體配置設定政策,不做記憶體配置設定,甚至配置設定到非記憶體裝置上,而malloc不行。
new調用構造函數,malloc不能;delete調用析構函數,而free不能。
malloc/free需要庫檔案stdlib.h的支援,new/delete則不需要!
注意:delete和free被調用後,記憶體不會立即回收,指針也不會指向空,delete或free僅僅是告訴作業系統,這一塊記憶體被釋放了,可以用作其他用途。但是由于沒有重新對這塊記憶體進行寫操作,是以記憶體中的變量數值并沒有發生變化,出現野指針的情況。是以,釋放完記憶體後,應該講該指針指向NULL。
30 說一說extern“C”
extern "C"的主要作用就是為了能夠正确實作C++代碼調用其他C語言代碼。加上extern "C"後,會訓示編譯器這部分代碼按C語言(而不是C++)的方式進行編譯。由于C++支援函數重載,是以編譯器編譯函數的過程中會将函數的參數類型也加到編譯後的代碼中,而不僅僅是函數名;而C語言并不支援函數重載,是以編譯C語言代碼的函數時不會帶上函數的參數類型,一般隻包括函數名。
這個功能十分有用處,因為在C++出現以前,很多代碼都是C語言寫的,而且很底層的庫也是C語言寫的,為了更好的支援原來的C代碼和已經寫好的C語言庫,需要在C++中盡可能的支援C,而extern "C"就是其中的一個政策。
C++代碼調用C語言代碼
在C++的頭檔案中使用
在多個人協同開發時,可能有的人比較擅長C語言,而有的人擅長C++,這樣的情況下也會有用到
31 請你來說一下C++中struct和class的差別
在C++中,class和struct做類型定義是隻有兩點差別:
預設繼承權限不同,class繼承預設是private繼承,而struct預設是public繼承
class還可用于定義模闆參數,像typename,但是關鍵字struct不能同于定義模闆參數 C++保留struct關鍵字,原因
保證與C語言的向下相容性,C++必須提供一個struct
C++中的struct定義必須百分百地保證與C語言中的struct的向下相容性,把C++中的最基本的對象單元規定為class而不是struct,就是為了避免各種相容性要求的限制
對struct定義的擴充使C語言的代碼能夠更容易的被移植到C++中
32 C++類内可以定義引用資料成員嗎?
可以,必須通過成員函數初始化清單初始化。
33 C++中類成員的通路權限
C++通過 public、protected、private 三個關鍵字來控制成員變量和成員函數的通路權限,它們分别表示公有的、受保護的、私有的,被稱為成員通路限定符。在類的内部(定義類的代碼内部),無論成員被聲明為 public、protected 還是 private,都是可以互相通路的,沒有通路權限的限制。在類的外部(定義類的代碼之外),隻能通過對象通路成員,并且通過對象隻能通路 public 屬性的成員,不能通路 private、protected 屬性的成員
34 什麼是右值引用,跟左值又有什麼差別?
左值和右值的概念:
左值:能取位址,或者具名對象,表達式結束後依然存在的持久對象;
右值:不能取位址,匿名對象,表達式結束後就不再存在的臨時對象; 差別:
左值能尋址,右值不能;
左值能指派,右值不能;
左值可變,右值不能(僅對基礎類型适用,使用者自定義類型右值引用可以通過成員函數改變);
35 面向對象的三大特征
封裝性:将客觀事物抽象成類,每個類對自身的資料和方法實行 protection (private , protected , public )。
繼承性:廣義的繼承有三種實作形式:實作繼承(使用基類的屬性和方法而無需額外編碼的能力)、可 視繼承(子窗體使用父窗體的外觀和實作代碼)、接口繼承(僅使用屬性和方法,實作滞後到子類實作)。
多态性:是将父類對象設定成為和一個或更多它的子對象相等的技術。用子類對象給父類對象指派 之後,父類對象就可以根據目前指派給它的子對象的特性以不同的方式運作。
36 說一說c++中四種cast轉換
C++中四種類型轉換是:static_cast, dynamic_cast, const_cast, reinterpret_cast
1、const_cast
用于将const變量轉為非const
2、static_cast
用于各種隐式轉換,比如非const轉const,void*轉指針等, static_cast能用于多态向上轉化,如果向下轉能成功但是不安全,結果未知;
3、dynamic_cast
用于動态類型轉換。隻能用于含有虛函數的類,用于類層次間的向上和向下轉化。隻能轉指針或引用。向下轉化時,如果是非法的***對于指針傳回NULL,對于引用抛異常***。要深入了解内部轉換的原理。
向上轉換:指的是子類向基類的轉換
向下轉換:指的是基類向子類的轉換
它通過判斷在執行到該語句的時候變量的運作時類型和要轉換的類型是否相同來判斷是否能夠進行向下轉換。
4、reinterpret_cast
幾乎什麼都可以轉,比如将int轉指針,可能會出問題,盡量少用;
5、為什麼不使用C的強制轉換?
C的強制轉換表面上看起來功能強大什麼都能轉,但是轉化不夠明确,不能進行錯誤檢查,容易出錯。
37 C++的空類有哪些成員函數
預設構造函數。
預設拷貝構造函數。
預設析構函數。
預設指派運算符。
預設取址運算符。
預設取址運算符 const 。
注意:有些書上隻是簡單的介紹了前四個函數。沒有提及後面這兩個函數。但後面這兩個函數也是 空類的預設函數。另外需要注意的是,隻有當實際使用這些函數的時候,編譯器才會去定義它們。
38 對c++中的smart pointer四個智能指針:shared_ptr,unique_ptr,weak_ptr,auto_ptr的了解
C++裡面的四個智能指針: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中後三個是c++11支援,并且第一個已經被11棄用。
智能指針的作用是管理一個指針,因為存在以下這種情況:申請的空間在函數結束時忘記釋放,造成記憶體洩漏。使用智能指針可以很大程度上的避免這個問題,因為智能指針就是一個類,當超出了類的作用域是,類會自動調用析構函數,析構函數會自動釋放資源。是以智能指針的作用原理就是在函數結束時自動釋放記憶體空間,不需要手動釋放記憶體空間。
auto_ptr(c++98的方案,cpp11已經抛棄)
采用所有權模式。
auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.”));
auto_ptr<string> p2;
p2 = p1; //auto_ptr不會報錯.
此時不會報錯,p2剝奪了p1的所有權,但是當程式運作時通路p1将會報錯。是以auto_ptr的缺點是:存在潛在的記憶體崩潰問題!
unique_ptr(替換auto_ptr)
unique_ptr實作獨占式擁有或嚴格擁有概念,保證同一時間内隻有一個智能指針可以指向該對象。它對于避免資源洩露(例如“以new建立對象後因為發生異常而忘記調用delete”)特别有用。
unique_ptr<string> p3 (new string ("auto")); //#4
unique_ptr<string> p4; //#5
p4 = p3;//此時會報錯!!
編譯器認為p4=p3非法,避免了p3不再指向有效資料的問題。是以,unique_ptr比auto_ptr更安全。
另外unique_ptr還有更聰明的地方:當程式試圖将一個 unique_ptr 指派給另一個時,如果源 unique_ptr 是個臨時右值,編譯器允許這麼做;如果源 unique_ptr 将存在一段時間,編譯器将禁止這麼做,比如:
unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1; // #1 not allowed
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You")); // #2 allowed
其中#1留下懸挂的unique_ptr(pu1),這可能導緻危害。而#2不會留下懸挂的unique_ptr,因為它調用 unique_ptr 的構造函數,該構造函數建立的臨時對象在其所有權讓給 pu3 後就會被銷毀。這種随情況而已的行為表明,unique_ptr 優于允許兩種指派的auto_ptr 。
注:如果确實想執行類似與#1的操作,要安全的重用這種指針,可給它賦新值。C++有一個标準庫函數std::move(),讓你能夠将一個unique_ptr賦給另一個。例如:
unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;
shared_ptr
shared_ptr實作共享式擁有概念。多個智能指針可以指向相同對象,該對象和其相關資源會在“最後一個引用被銷毀”時候釋放。從名字share就可以看出了資源可以被多個指針共享,它使用計數機制來表明資源被幾個指針共享。可以通過成員函數use_count()來檢視資源的所有者個數。除了可以通過new來構造,還可以通過傳入auto_ptr, unique_ptr,weak_ptr來構造。當我們調用release()時,目前指針會釋放資源所有權,計數減一。當計數等于0時,資源會被釋放。
shared_ptr 是為了解決 auto_ptr 在對象所有權上的局限性(auto_ptr 是獨占的), 在使用引用計數的機制上提供了可以共享所有權的智能指針。
成員函數:
use_count 傳回引用計數的個數
unique 傳回是否是獨占所有權( use_count 為 1)
swap 交換兩個 shared_ptr 對象(即交換所擁有的對象)
reset 放棄内部對象的所有權或擁有對象的變更, 會引起原有對象的引用計數的減少
get 傳回内部對象(指針), 由于已經重載了()方法, 是以和直接使用對象是一樣的.如 shared_ptrsp(new int(1)); sp 與 sp.get()是等價的
weak_ptr
weak_ptr 是一種不控制對象生命周期的智能指針, 它指向一個 shared_ptr 管理的對象. 進行該對象的記憶體管理的是那個強引用的 shared_ptr. weak_ptr隻是提供了對管理對象的一個通路手段。weak_ptr 設計的目的是為配合 shared_ptr 而引入的一種智能指針來協助 shared_ptr 工作, 它隻可以從一個 shared_ptr 或另一個 weak_ptr 對象構造, 它的構造和析構不會引起引用記數的增加或減少。weak_ptr是用來解決shared_ptr互相引用時的死鎖問題,如果說兩個shared_ptr互相引用,那麼這兩個指針的引用計數永遠不可能下降為0,資源永遠不會釋放。它是對對象的一種弱引用,不會增加對象的引用計數,和shared_ptr之間可以互相轉化,shared_ptr可以直接指派給它,它可以通過調用lock函數來獲得shared_ptr。
class B;
class A
{
public:
shared_ptr<B> pb_;
~A()
{
cout<<"A delete
";
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
cout<<"B delete
";
}
};
void fun()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
pb->pa_ = pa;
pa->pb_ = pb;
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
}
int main()
{
fun();
return 0;
}
可以看到fun函數中pa ,pb之間互相引用,兩個資源的引用計數為2,當要跳出函數時,智能指針pa,pb析構時兩個資源引用計數會減一,但是兩者引用計數還是為1,導緻跳出函數時資源沒有被釋放(A B的析構函數沒有被調用),如果把其中一個改為weak_ptr就可以了,我們把類A裡面的shared_ptr pb_; 改為weak_ptr pb_; 運作結果如下,這樣的話,資源B的引用開始就隻有1,當pb析構時,B的計數變為0,B得到釋放,B釋放的同時也會使A的計數減一,同時pa析構時使A的計數減一,那麼A的計數為0,A得到釋放。
注意:不能通過weak_ptr直接通路對象的方法,比如B對象中有一個方法print(),我們不能這樣通路,pa->pb_->print(); 英文pb_是一個weak_ptr,應該先把它轉化為shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();