1. 程式人生 > >C++面試常見題目問與答(彙總二)

C++面試常見題目問與答(彙總二)

上一次已經謝了一些東西了,感覺總結的差不多了,這一期主要是上一期的查漏補缺。主要是側重回答一些比較重一些的問題,比如智慧指標、RAII機制還有最長被問到的C++的多型。
首先是智慧指標。

1.智慧指標

上一期介紹一下智慧指標在用法的上要注意的部分,這次主要介紹一下為什麼。
這裡要提及的首先有三點:

  • shared_ptr是原始指標大小的兩倍。
  • 引用計數的記憶體必須被動態分配
  • 引用計數的改變(increments and decrements)必須是原子的

使用如下程式碼:

#include <memory>
#include <iostream>
using namespace std; int main() { int *rpw = new int(12); { shared_ptr<int> isptr1(rpw); cout << sizeof(rpw) << endl; cout << sizeof(isptr1) << endl; } system("pause"); return 0; }

在32bit的電腦上可以得到結果:
sizeof(shared_ptr)
明顯看到驗證了第一條。為什麼會是兩倍具體原因後面會分析

與unique_ptr類似,shared_ptr使用delete作為預設的資源解構函式,但是也可以使用使用者自己提供的刪除函式(deleter)。不過與unique_ptr不同的是,(unique_ptr的deleter是unique_ptr的型別的一部分)shared_ptr的deleter的型別不再是shared_ptr的型別的一部分。如下示例:

    auto del1 = [](int *p) {
        cout << "del1" << endl;
        delete p;
    };
    auto del2 = [](int
*p) { cout << "del2" << endl; delete p; }; shared_ptr<int> isptr1(new int(12), del1); shared_ptr<int> isptr2(new int(10), del2); vector<shared_ptr<int>> vsptr{ isptr1,isptr2 };

這裡的isptr1和isptr2的型別是一樣的,而對於unique_ptr則不同:

    auto del1 = [](int *p) {
        cout << "del1" << endl;
        delete p;
    };
    auto del2 = [](int *p) {
        cout << "del2" << endl;
        delete p;
    };
    unique_ptr<int, decltype(del1)> iuptr1(new int(12), del1);
    unique_ptr<int, decltype(del2)> iuptr2(new int(10), del2);

這裡的iuptr1和iuptr2是兩種不同的型別。
回到上面的分析中,為什麼shared_ptr的大小是raw pointer的兩倍的呢?原因主要是在shared_ptr的內部不只有一個類似原始指標的指向object的指標,還有一個指向Control Block的指標:
Control Block
如上所示,清晰的看到shared_ptr的sizeof返回值應該是2個指標的大小,其中一個指標指向需要指向的object,另外一個指標指向Control Block。Control Block中包含了對這個shared_ptr控制所必需的一些資訊,包括引用計數Reference Count、Weak Count、以及在Other Data中會存放使用者指定的deleter函式,分配器(allocator)等。所以從shared_ptr的開銷角度來說,接下主要是討論Control Block的建立已經建立帶來的問題和Control Block的開銷。
一般來說在三種情況下會建立Control Block

  • 通過raw pointer建立shared_ptr的時候
  • 通過make_shared建立shared_ptr的時候
  • 通過unique_ptr轉化建立shared_ptr的時候

上面三種情況都會建立Control Block,但是問題就出在這個Control Block上,稍後討論該部分的開銷。如果使用同一個raw pointer建立shared_ptr就會出現兩個不同的shared_ptr指向同一個raw pointer指向的資源,但是有兩個不同的Control Block,當一個的引用計數為0的時候,就會呼叫deleter釋放該資源,那麼當另一個shared_ptr也要釋放該資源的時候就會發生釋放已經被釋放的資源的錯誤,如下所示:

    int *rpw = new int(12);
    {
        shared_ptr<int> isptr1(rpw);
        shared_ptr<int> isptr2(rpw);
    }

顯然這種錯誤是致命的,因為有可能是發生在解構函式中,接下來還會導致資源洩漏,原本我們是為了防止資源洩漏的。所以,這裡建議盡我們在使用shared_ptr的時候應當避免使用raw pointer建立shared_ptr,也就是儘量避免。

2.詳解RAII:RAII是什麼?有什麼用?怎麼用?

RAII是Resource Acquisition Is Initialization的縮寫,也有人稱其為“資源獲取就是初始化”,是C++語言的一種管理資源、避免洩漏的慣用法。C++標準保證任何情況下,已構造的物件最終會銷燬,即它的解構函式最終會被呼叫。簡單的說,RAII 的做法是使用一個物件,在其構造時獲取資源,在物件生命期控制對資源的訪問使之始終保持有效,最後在物件析構的時候釋放資源。
(稍後補全)