1. 程式人生 > >C++ 編程技巧筆記記錄(持續更新)

C++ 編程技巧筆記記錄(持續更新)

算法 模板函數 () 不必要 過大 默認構造函數 函數 容易 []

前言:

希望通過閱讀《Effective C++》系列《Expectional C++》系列等書

挑選出一些個人覺得重要的條款/經驗/技巧進行記錄總結。


類/對象:

1.基類析構函數應總是public virtual,否則應為protected

當要釋放基類指針指向的對象時,為了按正確順序析構,必須得借助virtual從而先執行析構派生類再析構基類。

當你保證不會用基類指針釋放對象時,可將基類析構函數聲明protected,並且也無需耗費使用virtual。

2.編譯器會隱式生成默認構造,復制構造,復制賦值,析構函數

當你聲明了相應的函數時,就不會默認生成相應的函數。特殊的,當你聲明了構造函數(無論有無參數),都不會隱式生成默認構造函數。

不過隱式生成的函數比自己手寫的函數(即使行為一樣)效率要高,因為經過了編譯器特殊優化。

(c++11)當你需要禁用生成以上某個函數時, 可在聲明函數 = delete

例如:

Type(const Type& t) = delete;

(c++11)當你需要默認生成以上某個函數時,可在聲明函數 = default

例如:

Type() = default;

模板:

1.不要偏特化模板函數,而是選擇重載函數。

編譯器匹配函數時優先選擇非模板函數(重載函數),再選擇模板函數,最後再選擇偏特化模板函數。

當匹配到某個模板函數時,就不會再匹配選擇其他模板函數,即使另一個模板函數旗下有更適合的偏特化函數。

所以這很可能導致編譯器沒有選擇你想要的偏特化模板函數。

內存相關:

1.檢查new是否失敗通常是無意義的。

new幾乎總是成功的,現代大部分操作系統采取進程的惰式內存分配(即請求內存時不會立即分配內存,當使用時才慢慢吞吞分配)。

所以當使用new時,通常不會立即分配內存,從而無法真正檢測到是否內存將會耗盡。

2.盡量避免多次new同一種輕量級類型,而是先new一個大區域再分配多次。

每次new的時候,實際上還會額外分配出一個存放內存信息的區域,而多次分配內存給輕量級類型時,會造成臃腫的內存信息。

而且在刪除這些區域時,很容易造成很多塊內存碎片,導致內存利用率不高。

所以應當使用內存池的方式,先new一大塊區域,再從區域分配內存給輕量級類型。

STL標準庫:

1.(C++11)使用emplace/emplace_back/emplace_front而不是insert/push_back/push_front

emplace 最大的作用是避免產生不必要的臨時變量,因為它可以直接在容器相應的位置根據參數來構造變量。

而 insert / push_back / push_front 操作是會先通過參數構造一個臨時變量,然後將臨時變量移動到容器相應的位置。

2.在遍歷容器時刪除叠代器需謹慎

順序式容器刪除叠代器會破壞本身和後面的叠代器,節點式容器刪除叠代器會破壞本身,導致循環遍歷崩潰(循環遍歷依賴於容器原有的叠代器)。

兩個值得借鑒的正確做法:

auto it = vec.begin();
while (it != vec.end()){
    if (...){
        // 順序式容器的erase()會返回緊隨被刪除元素的下一個元素的有效叠代器
        it = vec.erase(it);
    }
    else{
        it++;
    }
}
auto it = list.begin();
while (it != list.end()){
    if (...) {
        t.erase(it++);
    }
    else {
        it++;
    }
}

3.容器的at()會檢查邊界,[]則不檢查邊界

優化與效率:

1.在後期遇到性能瓶頸,萬不得已時才使用inline

現代編譯器已經十分智能,很多時候該寫成inline的函數編譯器會自動幫你inline,不該inline的時候即使你顯式寫了inline編譯器也有可能認為不該inline。

也就是說顯式的寫出inline只是給編譯器一個建議,它不一定會采納。

因此在開發時不用過早優化,過早考慮inline,而是遇到性能瓶頸時才考慮使用顯式寫出inline,不過大部分這時候你更應該考慮的是你寫的算法效率。

2.可變類型編寫,使用boost::any而不是union

.....

C++ 編程技巧筆記記錄(持續更新)