如何寫出優(yōu)雅的C++代碼
本文轉(zhuǎn)載自微信公眾號(hào)「程序喵大人」,作者程序喵大人 。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序喵大人公眾號(hào)。
工欲善其事必先利其器,優(yōu)雅的代碼離不開靜態(tài)代碼檢查工具,大家可能平時(shí)使用較多的是cppcheck,但今天我想跟大家分享另一個(gè)靜態(tài)代碼檢查工具clang-tidy。
不同于cppcheck使用正則表達(dá)式進(jìn)行靜態(tài)代碼分析,clang-tidy是基于語法分析樹的靜態(tài)代碼檢查工具,雖然它的速度比正則表達(dá)式慢一些,但是它檢查的更準(zhǔn)確、全面,而且不僅可以做靜態(tài)檢查,還可以做一些修復(fù)工作,自行添加一些自定義檢查規(guī)則。
話不多說,上代碼:
- #include <iostream>
- int main() {
- int a = 1.2;
- return 0;
- }
這里有隱式類型轉(zhuǎn)換,可以使用clang-tidy來檢測:
- ~/test$ clang-tidy -checks=* test_lint.cpp --
- 7748 warnings generated.
- /home/wangzhiqiang/test/test_lint.cpp:20:13: warning: implicit conversion from 'double' to 'int' changes value from 1.2 to 1 [clang-diagnostic-literal-conversion]
- int a = 1.2;
- ^
- Suppressed 7747 warnings (7747 in non-user code).
- Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
這里也許你有疑問了,這不就是一個(gè)普通的編譯警告嘛,正常使用編譯器也可以檢查出來,那再看一段代碼:
- #include <iostream>
- int main() {
- char* d = NULL;
- return 0;
- }
我們都知道在C++中應(yīng)該更多的使用nullptr而不是NULL,這里使用了NULL而不是使用nullptr,可能我們?cè)陂_發(fā)過程中沒有注意到這種用法,所以clang-tidy派上了用場:
- ~/test$ clang-tidy -checks=* test_lint.cpp --
- 7748 warnings generated.
- /home/wangzhiqiang/test/test_lint.cpp:20:15: warning: use nullptr [modernize-use-nullptr]
- char* d = NULL;
- ^~~~~
- nullptr
- Suppressed 7747 warnings (7747 in non-user code).
- Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
再舉一個(gè)例子:
- struct Base {
- virtual void func() {
- }
- };
- struct Derive : Base {
- virtual void func() {
- }
- };
這里可能我們乍一看沒有任何問題,其實(shí)在C++11里派生類繼承父類,重寫了某些函數(shù)時(shí)最好加上override關(guān)鍵字,通過clang-tidy還是可以檢測出來:
- ~/test$ clang-tidy -checks=* test_lint.cpp --
- 7749 warnings generated.
- /home/wangzhiqiang/test/test_lint.cpp:14:18: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' [hicpp-use-override]
- virtual void func() {
- ~~~~~~~~~~~~~^
- override
- Suppressed 7747 warnings (7747 in non-user code).
- Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
該工具還可以檢查代碼是否符合編碼規(guī)范,例如Google編碼規(guī)范等,看這段頭文件相關(guān)代碼:
- #include <iostream>
- #include <string>
- #include <memory>
這里其實(shí)有一點(diǎn)點(diǎn)問題,頭文件引用順序不滿足編碼規(guī)范,這里其實(shí)clang-format都可以檢測出來,但clang-tidy也可以檢測出來,通過-fix還可以進(jìn)行自動(dòng)修復(fù):
- ~/test$ clang-tidy -checks=* test_lint.cpp --
- 8961 warnings generated.
- /home/wangzhiqiang/test/test_lint.cpp:2:1: warning: #includes are not sorted properly [llvm-include-order]
- #include <string>
- ^ ~~~~~~~~
- Suppressed 8960 warnings (8960 in non-user code).
- Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well
它還可以檢測隱藏的內(nèi)存泄漏:
- int main() {
- char* ct = (char*)malloc(323);
- return 0;
- }
這是使用clang-tidy的檢測結(jié)果:
- ~/test$ clang-tidy -checks=* test_lint.cpp --
- 7756 warnings generated.
- /home/wangzhiqiang/test/test_lint.cpp:20:5: warning: initializing non-owner 'char *' with a newly created 'gsl::owner<>' [cppcoreguidelines-owning-memory]
- char* ct = (char*)malloc(323);
- ^
- /home/wangzhiqiang/test/test_lint.cpp:20:5: warning: use auto when initializing with a cast to avoid duplicating the type name [hicpp-use-auto]
- char* ct = (char*)malloc(323);
- ^~~~~
- auto
- /home/wangzhiqiang/test/test_lint.cpp:20:11: warning: Value stored to 'ct' during its initialization is never read [clang-analyzer-deadcode.DeadStores]
- char* ct = (char*)malloc(323);
- ^
- /home/wangzhiqiang/test/test_lint.cpp:20:11: note: Value stored to 'ct' during its initialization is never read
- /home/wangzhiqiang/test/test_lint.cpp:20:16: warning: C-style casts are discouraged; use static_cast [google-readability-casting]
- char* ct = (char*)malloc(323);
- ^~~~~~~~~~~~~ ~
- static_cast<char*>( )
- /home/wangzhiqiang/test/test_lint.cpp:20:16: warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]
- /home/wangzhiqiang/test/test_lint.cpp:20:23: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
- char* ct = (char*)malloc(323);
- ^
- /home/wangzhiqiang/test/test_lint.cpp:21:5: warning: Potential leak of memory pointed to by 'ct' [clang-analyzer-unix.Malloc]
- return 0;
- ^
- /home/wangzhiqiang/test/test_lint.cpp:20:23: note: Memory is allocated
- char* ct = (char*)malloc(323);
- ^
- /home/wangzhiqiang/test/test_lint.cpp:21:5: note: Potential leak of memory pointed to by 'ct'
- return 0;
- ^
- Suppressed 7747 warnings (7747 in non-user code).
- Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well
clang-tidy還有很多高端功能,大概可以檢測出250種問題,大體主要分為幾大類:
- abseil:檢測abseil庫的相關(guān)問題
- android:檢測Android相關(guān)問題
- boost:檢測boost庫的相關(guān)問題
- cert:檢測CERT的代碼規(guī)范
- cpp-core-guidelines:檢測是否違反cpp-core-guidelines
- google:檢測是否違反google編碼規(guī)范
- llvm:檢測是否違反llvm編碼規(guī)范
- performance:檢測性能相關(guān)的問題
- readability:檢測與可讀性相關(guān),但又不屬于某些編碼規(guī)范的問題
- modernize:檢測是否使用現(xiàn)代C++11相關(guān)的代碼問題
而且適用于Windows/Linux/MacOS多平臺(tái),還支持命令行,CLion/VSCode/VSStudio插件等,檢測規(guī)則還可以定制,重要的是免費(fèi)開源,快去用起來吧,寫出優(yōu)雅的C++代碼~
參考資料:
https://clang.llvm.org/extra/clang-tidy/
https://www.bilibili.com/video/av96166240/