為什麼C++ map容器emplace()、emplace_hint()的執行效率比insert()高?

2020-07-16 10:05:21
上一節在學習 C++STL map 容器的 emplace() 和 emplace_hint() 的基本用法時,還遺留了一個問題,即為什麼 emplace() 和 emplace_hint() 的執行效率會比 insert() 高?

原因很簡單,它們向 map 容器插入鍵值對時,底層的實現方式不同:
  • 使用 insert() 向 map 容器中插入鍵值對的過程是,先建立該鍵值對,然後再將該鍵值對複製或者移動到 map 容器中的指定位置;
  • 使用 emplace() 或 emplace_hint() 插入鍵值對的過程是,直接在 map 容器中的指定位置構造該鍵值對。

也就是說,向 map 容器中插入鍵值對時,emplace() 和 emplace_hint() 方法都省略了移動鍵值對的過程,因此執行效率更高。下面程式提供了有利的證明:
#include <iostream>
#include <map>  //map
#include <string> //string
using namespace std;

class testDemo
{
public:
    testDemo(int num) :num(num) {
        std::cout << "呼叫建構函式" << endl;
    }
    testDemo(const testDemo& other) :num(other.num) {
        std::cout << "呼叫拷貝建構函式" << endl;
    }
    testDemo(testDemo&& other) :num(other.num) {
        std::cout << "呼叫移動建構函式" << endl;
    }
private:
    int num;
};

int main()
{
    //建立空 map 容器
    std::map<std::string, testDemo>mymap;

    cout << "insert():" << endl;
    mymap.insert({ "http://c.biancheng.net/stl/", testDemo(1) });
   
    cout << "emplace():" << endl;
    mymap.emplace( "http://c.biancheng.net/stl/:", 1);

    cout << "emplace_hint():" << endl;
    mymap.emplace_hint(mymap.begin(), "http://c.biancheng.net/stl/", 1);
    return 0;
}
程式輸出結果為:

insert():
呼叫建構函式
呼叫移動建構函式
呼叫移動建構函式
emplace():
呼叫建構函式
emplace_hint():
呼叫建構函式

分析一下這個程式。首先,我們建立了一個儲存 <string,tempDemo> 型別鍵值對的空 map 容器,接下來分別用 insert()、emplace() 和 emplace_hint() 方法向該 map 容器中插入相同的鍵值對。

從輸出結果可以看出,在使用 insert() 方法向 map 容器插入鍵值對時,整個插入過程呼叫了 1 次 tempDemo 類別建構函式,同時還呼叫了 2 次移動建構函式。實際上,程式第 28 行程式碼底層的執行過程,可以分解為以下 3 步:
//構造類物件
testDemo val = testDemo(1); //呼叫 1 次建構函式
//構造鍵值對
auto pai = make_pair("http://c.biancheng.net/stl/", val); //呼叫 1 次移動建構函式
//完成插入操作
mymap.insert(pai); //呼叫 1 次移動建構函式
而完成同樣的插入操作,emplace() 和 emplace_hint() 方法都只呼叫了 1 次建構函式,這足以證明,這 2 個方法是在 map 容器內部直接構造的鍵值對。

因此,在實現向 map 容器中插入鍵值對時,應優先考慮使用 emplace() 或者 emplace_hint()。