你需要的代碼靜態檢查


代碼靜態檢查

使用cppcheck給工程代碼做靜態檢查,主要發現了以下幾個問題:

1. 使用C風格的類型轉換

警告如下:

C-style pointer casting detected. C++ offers four different kinds of casts as replacements: static_cast, const_cast, dynamic_cast and reinterpret_cast. A C-style cast could evaluate to any of those automatically, thus it is considered safer if the programmer explicitly states which kind of cast is expected. See also: https://www.securecoding.cert.org/confluence/display/cplusplus/EXP05-CPP.+Do+not+use+C-style+casts.

應該使用C++提供的static_cast, const_cast, dynamic_cast 和 reinterpret_cast 做類型轉換,明確轉換的類型。

2. 迭代器使用后置疊加(疊減)運算符

警告如下:

Prefix ++/-- operators should be preferred for non-primitive types. Pre-increment/decrement can be more efficient than post-increment/decrement. Post-increment/decrement usually involves keeping a copy of the previous value around and adds a little extra code.

迭代器前置++和后置++ 的運行效率是不同的,前置++效率更高,因為后置運算符需要做一個臨時的類對象拷貝。

3. 直接在函數參數中使用C風格字符串。

警告如下:

The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly passing the string.

比如一個函數的參數類型是strng,調用時實參直接使用了C風格的字符串,於是就會有以上提示,主要還是因為這里會造成一個不必要的字符串拷貝,降低運行效率。

4. 使用無用的find

很多時候,我們會寫下面的find代碼,值不等於-1則說明找到了該字符串,

if(std::string::npos != dataStr.find("what"))
    //do something

該代碼段會導致報警:

Either inefficient or wrong usage of string::find(). string::compare() will be faster if string::find's result is compared with 0, because it will not scan the whole string. If your intention is to check that there are no findings in the string, you should compare with std::string::npos.

代碼本身不會出錯,但是效率上是不被認可的,如cppcheck所說,如果你希望檢查某個字符串是不是某個期望的字符串,那你應該使用compare函數,因為這樣更快。

5. 函數參數使用傳值而不是傳引用

警告如下:

Parameter 'strShowTime' is passed by value. It could be passed as a const reference which is usually faster and recommended in C++.

C++給了引用傳參,而你不使用,那就是你的問題了。

6. 構造函數沒有使用初始化成員變量列表,而是習慣於寫入構造函數體

警告有:

Member variable 'CActiveComboItemXml::m_phorActivecombo' is not initialized in the constructor.

或者如:

When an object of a class is created, the constructors of all member variables are called consecutively in the order the variables are declared, even if you don't explicitly write them to the initialization list. You could avoid assigning 'm_strHrefOnPanReady' a value by passing the value to the constructor in the initialization list.

這個問題很普遍,因為很多程序員不習慣在構造函數的初始化列表中初始化成員變量,有的是直接忽略掉,有的則是在構造函數的函數體中去使用賦值運算符賦值,可是這個時候已經不是初始化了,而是賦值階段了。這是個很危險的習慣!

7.使用memset清空含有string(wstring)類型成員的結構體

警告為:
Using 'memset' on struct that contains a 'std::wstring'. [memsetClass]

在C語言中,使用memset清空內存是常用操作,在C++中,和malloc一樣,使用memset直接操作內存是有很大風險的,因為它們都只是在內存層面做了改動,但是對類對象本身卻沒有任何行動,也就是說,他們不會調用類對象的構造或者析構函數,如果我們的結構體中含有string類型的成員,直接使用memset很可能導致內存泄漏!

這里涉及到一個POD參考的概念。如果一個類不是POD的,那么就不應該使用如mem*,malloc等內存操作函數,否則,我們將得不到我們想要的東西,甚至引發錯誤。

舉一個很簡單的例子:

struct struTest
{
string str1;
};

void TestStructString()
{
    struTest tt;    
    size_t sizestru = sizeof(tt);
    tt.str1 = "this OK";
    struTest* tt2 = new struTest;
    memcpy(tt2,&tt,sizestru);
    cout << "tt2 str1 is:"<<tt2->str1 <<"\n";
    delete tt2;
	//程序奔潰!!!
}

測試程序以memcpy函數為例,運行本程序,雖然tt2可以正常的將str1拷貝進來,但是最后程序奔潰了!!
想想原因是為何? 程序崩潰在tt的析構函數中,因為無法探究memcpy到底做了哪些具體操作,所以我們可以猜測它是將tt的內存區域完整的拷貝到了tt2內,但是,tt2中的strin類型的成員str1並沒有完成自己的構造,而是通過內存拷貝的方式完整的使用了tt的數據,那么執行完delete ,tt2的str1也會析構自身,這時其析構的其實是tt的str1。等到tt再去析構自己時,奔潰就發生了!以上只是自己的猜測。可能不是事實,但是,當我把delete tt2注釋后,崩潰就消失了,這也能證明自己上面的論述。

問題怎么處理?

在C++中,除非是明確的POD類型,否則放棄使用mem*系包括malloc等傳統C內存操作函數。

首先,我們需要弄明白,我們為什么需要使用memet,因為我們需要將這個結構體的數據清零,所以我們真正需要的是一個結構體(類)的構造函數!在類中寫一個結構體,將類里的所有成員變量進行列表初始化就可以解決這個問題了。
話說回來,就好像,我們在寫C代碼時, 如果結構體某個成員類型需要是結構體類型,我們使用了該結構體指針一樣,我們同樣可以使用string類型的指針來表示該成員類型。畢竟在VS2010環境下,一個string類型的內存占用就是32byte,而一個string*只有4byte。如果擔心hold不住指針類型,可以使用智能指針來折中,如shared_ptr,內存的占用將減小到8。事實上,使用智能使用已經是一個最優方案了。

這幾個問題大量出現在cppcheck的問題列表中,是我們經常犯的編程問題,應從代碼風格上進行規避。
當然了,可能的錯誤(警告)是由不當的編碼風格和不扎實的C++編碼基礎導致的,通過靜態檢查我們自己的代碼,可以最大層度的寫出易讀且不容易出錯的代碼。

推薦大家使用cppcheck!


免責聲明!

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



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