摘要:近期踩到了一些比較隱晦的C++的坑,可把我們團隊給坑慘了~~
近期我們團隊進行版本質量加固時,踩到了一些比較隱晦的C++的坑,特總結分享在此,供大家參考,
1. string的字串拼接,導致coredump

該問題的核心點在于第9行,竟然是可以編譯通過,其原因是x+"-",會被轉成char*,然后與to_string疊加導致BUG,
2. map的迭代器洗掉
map要洗掉一個元素,通常通過erase()函式來完成,但是要注意,如果我們傳入了一個iterator作為erase的引數來洗掉當前迭代器所指向的元素,洗掉完成后iterator會失效,產生未定義行為,
正確的使用方法應該是接收erase()的回傳值,讓iterator指向被洗掉元素的下一個元素或者end(),
for ( auto iter = m.begin(); iter != m.end(); iter++) {
if (...)
iter = m.erase(iter);
}
但是上述代碼仍然有錯誤,因為如果觸發了洗掉,那么iter再下一輪回圈時會指向下下個元素,所以正確的寫法應該是:
for ( auto iter = m.begin(); iter != m.end();) {
if (...) {
iter = m.erase(iter);
continue ;
} else {
iter++;
}
}
3. stringstream的性能問題
- stringstream的清空是clear之后,置空,
- stringstream在任何情況下都比snprintf慢,
- memset是個很慢的函式,寧愿新創建物件,
- 上述測驗結果是單執行緒,改成多執行緒,同樣成立,
- str += “a”, 比 str =str+ “a” 效率高很多,后者會創建新物件,
4. 智能指標(shared_ptr)使用注意
4.1盡量使用make_shared初始化
提高性能
std::shared_ptr<Widget> spw(newWidget);
需要分配兩次記憶體,每個std::shared_ptr都指向一個控制塊,控制塊包含被指向物件的參考計數以及其他東西,這個控制塊的記憶體是在std::shared_ptr的建構式中分配的,因此直接使用new,需要一塊記憶體分配給Widget,還要一塊記憶體分配給控制塊
autospw = std::make_shared<Widget>();
一次分配就足夠了,這是因為std::make_shared申請一個單獨的記憶體塊來同時存放Widget物件和控制塊,這個優化減少了程式的靜態大小,因為代碼只包含一次記憶體分配的呼叫,并且這會加快代碼的執行速度,因為記憶體只分配了一次,另外,使用std::make_shared消除了一些控制塊需要記錄的資訊,這樣潛在地減少了程式的總記憶體占用,
例外安全
processWidget(std::shared_ptr<Widget>( new Widget), //潛在的資源泄露
computePriority());
上述代碼存在記憶體泄漏的風險,上述代碼執行分為3個步驟:
1. new Widget
2. shared_ptr構造
3. computePriority
編譯器不需要必須產生這樣順序的代碼,但“new Widget”必須在std::shared_ptr的建構式被呼叫前執行,如果編譯器產生的順序代碼如下:
1. new Widget
2. 執行computePriority,
3. 執行std::shared_ptr的建構式,
如果執行步驟2:computePriority的時候程式出現例外,則在第一步動態分配的Widget就會泄露了,因為它永遠不會被存放到在第三步才開始管理它的shared_ptr中
4.2 父類之類智能指標轉換
C++中是允許裸指標,因此裸指標之間轉換方法同C語言指標強轉,智能指標轉換不能通過上述方法進行強轉,必須通過庫提供轉換函式進行轉換, C++11的方法是:std::dynamic_pointer_cast;boost中的方法是:boost::dynamic_pointer_cast
#include <memory>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
class Base {
public :
Base(){}
virtual ~Base() {}
};
class D : public Base {
public :
D(){}
virtual ~D() {}
};
int main()
{
//方式一:先初始化子類智能指標,然后呼叫dynamic_pointer_cast轉換成基類智能指標物件
std::shared_ptr<D> d1 = std::make_shared<D>();
std::shared_ptr<Base> b1 = std::dynamic_pointer_cast<Base>(d1);
//方式二:先new子類D的指標,然后呼叫shared_ptr的建構式初始化基類智能指標
std::shared_ptr<Base> b2 = shared_ptr<Base>( new D());
return 0;
}
結論
方式一和方式二均能夠實作基類智能指標指向子類,但建議采用方式1,通過std::make_shared的方式構造智能指標,然后進行轉換;
5. map的安全查找辦法
即map[key]這種寫法,就是會創建元素(且不一定初始化),因此在業務邏輯是希望查找的時候,就老老實實用find,不然會有臟資料寫入,
6. string 的指標構造
std::string 的構造方式,除了與其它順序容器相近的方式之外,提供了三種額外的構造方式:
string s(cp, n): s 是cp指向的陣列中前n個字符的拷貝,該陣列至少應該包含n個字符
string s(s2, pos2):s 是string s2從下標pos2開始的字符的拷貝,若pos2>s2.size(),建構式的行為未定義
string s(s2, pos2, len2):s 是string s2從下標pos2開始len2個字符的拷貝,若pos2>s2.size(),建構式的行為未定義,不管len2的值是多少,建構式至多拷貝s2.size()-pos2個字符
std::string 未提供 string(cp, pos2, len2) 這種構造方式,如果代碼中使用了該方式,最侄訓將 cp 指向的陣列構造成一個string,然后呼叫string(s2, pos2, len2)這種構造方式,
不提供string(cp, pos2, len2)這種構造方式原因在于:使用這種方式構造容易出現問題,cp是一個指標,通常使用時,能獲得其陣列長度并檢查傳入引數;若傳入兩個引數,容易出現越界,
7. 變數初始化
變數初始化總是沒錯的,不管后面是否會修改該值,尤其是int等內建的型別,在類或struct中及容易忽略初始化,使變數成為隨機值,產生不可預知的錯誤,變數請初始化!變數請初始化!!變數請初始化!!!
點擊關注,第一時間了解華為云新鮮技術~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/202841.html
標籤:其他
上一篇:2020CCPC綿陽 K- Knowledge is Power
下一篇:NodeJS實作JWT原理
