工作中遇到的C++語言基礎和常見錯誤

C++歷史及標準

這裏簡單列一下C++發展進程中的幾次重大事件以及我常使用的典型特性,各個標準支持的具體細節可參閱ISO標準文檔。

  • C With Classes:支持C++基礎語言特性,包括多態、異常處理、模板、命名空間等
  • C++98:STL、RTTI、模板、異常處理及其它標準庫實現
  • C++03:修復C++98中的缺陷及支持TR1
  • C++11:auto、range-for、rvalue、lambda、shared_ptr、concurrent
  • C++14:變量模板、多態lambda及增強的庫實現
  • 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++有很多特性,上述只是列出了極小一部分使用過程中經常出現問題的一些用法,比如導致崩潰或者內存泄漏等,以及可以使用性能更高的一些建議,更多的用法將在後續逐漸總結出來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章