C++面試基礎篇(一)


1. static關鍵字的作用

(1)全局靜態變量

 在全局變量前面加上關鍵字static, 全局變量就定義為一個全局靜態變量
 在靜態存儲區,在整個程序運行期間一致存在。
 初始化:未初始化的全局靜態變量會被自動初始化為0(自動對象的值是任意的,除非他被顯示初始化)
 作用域:全局靜態變量在聲明他的文件之外是不可見的,准確的說是從定義之處開始,到文件結尾。

(2)局部靜態變量

在局部變量前加上關鍵字static,局部變量就成為一個靜態的局部變量
內存中的位置:靜態存儲區
初始化:未初始化的靜態局部變量會被自動初始化為0
作用域:作用域仍為局部作用域,當定義它的函數或者語句塊結束的時候,作用域結束。但是當局部靜態變量離開作用域后,並沒有被銷毀,而是仍然駐留在內存中,只不過我們不能再對它進行訪問,直到該函數再次被調用,並且值不變。

(3)靜態函數

在函數返回值類型前加關鍵字static,函數就被定義為靜態函數。函數的定義和聲明在默認情況下都是extern的,但是靜態函數只能在聲明它的文件中可見,不能被其他文件所用。 
函數的實現使用static修飾,那么這個函數只可以在本cpp內使用,不會同其他cpp中的同名函數引起沖突。
Warning:不要在頭文件中生命static的全局函數,不要在cpp內聲明非static的全局函數,如果你要在多個cpp中復用該函數,就把它的聲明提到頭文件里去,否則cpp內部聲明需加上static修飾。

(4)類的靜態成員

在類中,靜態成員可以實現多個對象之間的數據共享,並且使用靜態數據成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態成員是類的所有對象中共享的成員,而不是某個對象的成員,對多個對象來說,靜態數據成員只存儲一處,供所有對象公用。

(5)類的靜態函數

靜態成員函數和靜態數據成員一樣,他們都屬於類的靜態成員,他們都不是對象成員,因此,對靜態成員的引用不需要用對象名。
在靜態成員函數的實現中,不能直接引用類中說明的非靜態成員,可以引用類中聲明的靜態成員。如果靜態成員函數中要引用非靜態成員時,可以通過對象來引用。可以從中看出,調用靜態成員函數使用如下格式:<類名>::<靜態成員函數名>(<參數表>)

2.C++和C的區別

(1)設計思想

C++是面向對象的語言,而C是面向過程的結構化編程語言

(2)語法上

C++具有封裝、繼承和多態三種特性, C++相比於C,增多了許多類型安全的功能,比如強制類型轉換, C++支持范式編程,比如模板類,函數模板等。

3.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
用於動態類型轉換,只能用於含有虛函數的類,用於類層次間的向上和向下轉化。只能轉指針或引用。向上轉換:指的是子類向基類轉換。 向下轉換:指的是基類向子類轉換。  他通過判斷在執行到該語句的時候變量的運行時類型和要轉換的類型是否相同來判斷是否能夠向下轉換。    
4)reinterpret_cast
幾乎什么都可以轉,比如將int轉指針,可能會出問題,盡量少用。
5)為什么不用C的強制轉換?
C的強制轉換表面上看起來功能強大什么都能轉換,但轉化不夠明確,不能進行錯誤檢查,容易出錯。

4.C/C++中指針和引用的區別

1). 指針有自己的一塊空間,而引用只是一個別名
2). 使用sizeof看一個指針的大小是4,而引用則是被引用對象的大小
3). 指針可以被初始化為NULL,而引用必須被初始化且必須是一個已有對象的引用
4). 作為參數傳遞時,指針需要被解引用才可以對對象進行操作,而直接對引用的修改都會改變引用所指向的對象。
5). 可以有const指針,但是沒有const引用
6). 指針在使用中可以指向其他對象,但是引用只能是一個對象的引用,不能被改變
7). 指針可以有多級指針(**p ),而引用止於一級
8). 指針和引用使用++運算符的意義是不一樣的
9). 如果返回動態內存分配的對象或者內存,必須使用指針,引用可能引起內促泄露。

5.C++中的smart pointer四個智能指針shared_ptr, unique_ptr, weak_ptr, auto_ptr

C++中的四個智能指針shared_ptr, weak_ptr,  unique_ptr, auto_ptr 其中前面三個是C++11支持並且最后一個已經被C++11拋棄。
智能指針的作用是管理一個指針,因為存在以下這種情況:申請的空間在函數結束時忘記釋放,造成內存泄露。使用智能指針可以很大程度上的避免這個問題。因為智能指針就是一個類,當超出了類的作用域時,類會自動調用析構函數,析構函數會自動釋放資源。所以智能指針的作用原理就是在函數結束時自動釋放內存空間。不需要手動釋放內存空間。
1). auto_ptr(C++11已經拋棄)
采用所有權模式
    auto_ptr<string> p1(new string("I love you!"))
    auto_ptr<string>p2;
    p2 = p1;  // auto_ptr不會報錯
此時不會報錯, p2剝奪了p1的所有權,但是當程序運行時訪問p1將會報錯。所以auto_ptr的缺點是:存在潛在的內存崩潰問題。
2). unique_ptr(替換auto_ptr)
unique_ptr實現獨占式擁有或嚴格擁有概念,保證同一時間內只有一個智能指針可以指向該對象。它對於避免資源泄漏(例如:以new創建對象以后因為發生異常而忘記調用delete)特別有用。

采用所有權模式,還是上面那個例子
unique_ptr <string>  p3 (new string ("I love you!"));
unique_ptr <string>  p4;
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;
3). 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()是等價的
4). 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\n";
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
    cout<<"B delete\n";
}
};
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();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM