C++曆史及标準
這裡簡單列一下
C++
發展程序中的幾次重大事件以及我常使用的典型特性,各個标準支援的具體細節可參閱ISO标準文檔。
-
:支援C++基礎語言特性,包括多态、異常處理、模闆、命名空間等C With Classes
-
:STL、RTTI、模闆、異常處理及其它标準庫實作C++98
-
:修複C++98中的缺陷及支援TR1C++03
-
:auto、range-for、rvalue、lambda、shared_ptr、concurrentC++11
-
:變量模闆、多态lambda及增強的庫實作C++14
-
:折疊表達式、類模闆實參推導C++17
-
:<=>、協程、概念C++20
參數傳遞與傳回值
- 避免産生臨時變量導緻備援性能開銷
int setupMVAudioStream(std::string path); // BAD
int setupMVAudioStream(std::string const& path); // GOOD
- 傳回值為類對象時确定使用RVO特性
// 如果此時函數體實作編譯器未使用RVO,則會出現備援性能開銷,BAD
std::list<MVStreamOption*> generateMVStreamList(std::list<MVStreamOption*> *optionList);
// 使用引用傳遞參數傳回結果,不會出現備援性能開銷,GOOD
void generateMVStreamList(std::list<MVStreamOption*>& outList,
std::list<MVStreamOption*> *optionList);
- 函數具有傳回類型時需明确給出傳回值,避免外部使用錯誤的傳回值或者函數無法正常執行結束
int EditorService::updateRenderStreams(FileStreamList &streamList)
{
// 執行一些操作,沒有return語句或者存在多個可能無法執行到的非全局生存期return語句
if (condition)
{
return -1; // 當condition為false時不執行
}
// BAD
}
// 始終應該存在一個函數内全局生存期的return語句,避免其它非全局生存期的return語句未執行
int EditorService::updateRenderStreams(FileStreamList &streamList)
{
// 執行一些操作,可能存在多個多生存期管理的return語句
if (condition)
{
return -1; // 當condition為false時不執行
}
return 0; // GOOD
}
- 傳回類成員變量時應該傳回引用或者常量引用或者指針
// BAD,調用unordered_map的拷貝構造函數導緻額外性能開銷
std::unordered_map<Node*, int> Node::GetActiveChildren()
{
return mActiveChildren;
}
// GOOD,傳回引用和常量引用,不會産生臨時對象
std::unordered_map<Node*, int>& Node::GetActiveChildren()
{
return mActiveChildren;
}
std::unordered_map<Node*, int> const& Node::GetActiveChildren() const
{
return mActiveChildren;
}
基類聲明虛析構函數避免産生記憶體洩漏
struct Base { // BAD: implicitly has a public nonvirtual destructor
virtual void f();
};
struct D : Base {
string s {"a resource needing cleanup"};
~D() { /* ... do some cleanup ... */ }
// ...
};
void use()
{
unique_ptr<Base> p = make_unique<D>();
// ...
} // p's destruction calls ~Base(), not ~D(), which leaks D::s and possibly more
構造和析構函數中避免調用虛函數
class Base {
public:
virtual void f() = 0; // not implemented
virtual void g(); // implemented with Base version
virtual void h(); // implemented with Base version
};
class Derived : public Base {
public:
void g() override; // provide Derived implementation
void h() final; // provide Derived implementation
Derived()
{
// BAD: attempt to call an unimplemented virtual function
f();
// BAD: will call Derived::g, not dispatch further virtually
g();
// GOOD: explicitly state intent to call only the visible version
Derived::g();
// ok, no qualification needed, h is final
h();
}
};
優先使用初始化清單而不是指派構造對象
class B { // BAD
string s1;
public:
B(const char* p) { s1 = p; } // BAD: default constructor followed by assignment
// ...
};
class C { // UGLY, aka very bad
int* p;
public:
C() { cout << *p; p = new int{10}; } // accidental use before initialized
// ...
};
class D { // Good
string s1;
public:
A(string_view v) : s1{v} { } // GOOD: directly construct
// ...
};
使用auto作為傳回類型推導時增加cv修飾避免産生臨時變量
std::unordered_map<Node*, int> const& Node::GetActiveChildren() const
{
return mActiveChildren;
}
// BAD: 此時children類型實際為std::unordered_map<Node*, int>,退化為采用模闆類型推導沒有cv屬性
auto children = node->GetActiveChildren();
優先使用emplace接口替換push/insert提高性能
std::list<std::string> ls;
std::string s("abc");
ls.push_back(s); // BAD
ls.push_back(std::move(s)); // GOOD
ls.push_back("abc"); // GOOD
ls.emplace_back("abc"); // BETTER
使用make_unique/make_shared構造智能指針管理對象
// Not exception-safe: the compiler may interleave the computations of arguments as follows:
//
// 1. allocate memory for Foo,
// 2. construct Foo,
// 3. call bar,
// 4. construct unique_ptr<Foo>.
//
// If bar throws, Foo will not be destroyed, and the memory-allocated for it will leak.
f(unique_ptr<Foo>(new Foo()), bar()); // BAD
// Exception-safe: calls to functions are never interleaved.
f(make_unique<Foo>(), bar()); // GOOD
使用empty代碼size判斷STL容器是否為空
std::list<int> ls;
// ...
// BAD: 不同的STL标準實作稍有差異,比如Android下的list.size的時間複雜度為O(N)
if (ls.size() > 0)
{
// ...
}
// GOOD:時間複雜度為O(1)
if (!ls.empty())
{
// ...
}
總結
C++有很多特性,上述隻是列出了極小一部分使用過程中經常出現問題的一些用法,比如導緻崩潰或者記憶體洩漏等,以及可以使用性能更高的一些建議,更多的用法将在後續逐漸總結出來。