如何使用map的两个主要的emplace成员函数

336 阅读6分钟

在C++中,emplace()和insert()函数做了一件类似的事情。当程序员不关心emplace所具有的优势时,他可以使用insert()。Emplace在感兴趣的容器中构造感兴趣的元素,同时从某处插入元素的副本或将元素移到感兴趣的容器中。

复制概念

考虑以下两个字符的列表。

        list<char> lA = {'A', 'B', 'C', 'D'};

        list<char> lB = {'E', 'F', 'G', 'H'};

lB中的'G'可以被复制并放在lA中的'D'前面,以拥有。

        list<char> lA = {'A', 'B', 'C', 'G', 'D'};
        list<char> lB = {'E', 'F', 'G', 'H'};

移动概念

lB中的'G'可以去掉,放在lA中的'D'前面,以有。

        list<char> lA = {'A', 'B', 'C', 'G', 'D'};
        list<char> lB = {'E', 'F', 'H'};

元素中的值

复制和移动并不像上面显示的那样直白。在实际情况中,每个值(例如,字符)都在一个元素中。所以,如果一个字符列表有四个字符,那么这个列表就有四个元素。如果一个字符列表有三个字符,那么这个列表就有三个元素。

一个元素可以是一个有三个数据成员的结构。第一个数据成员是一个指针,指向列表中的前一个元素。第二个数据成员持有值,在本例中是字符。第三个数据成员是一个指针,指向列表中的下一个元素。

因此,每个字符,如上面的'G',将由一个结构的第二个数据成员持有。在上面的原始列表lB中,对于'G',结构的第一个数据成员将指向有'F'的元素,而结构的第三个数据成员将指向有'H'的元素。

插入和移位

当insert()必须按照上面的表达方式进行复制时,理论上,"G "的元素,即完整的结构,将被复制并放置在 "D "元素的前面。在实践中,当一个新的对应元素被认为放在'D'元素前面之后,新的G的结构的第三个数据成员将被指向'D'元素;而新的G的结构的第一个数据成员将被指向'C'元素(列表lA)。

当insert()需要如上所述进行移动时,会进行这里所解释的复制,然后删除列表lB中的'G'元素。

另一方面,Emplace()并不真正需要复制或移动任何元素。它只需要向程序指出感兴趣的值是字符'G'。然后,程序将在列表lA中的'D'元素前面构造一个新的元素,其值为'G'。也就是说,它将在'D'元素前面创建一个新的'G'结构,第三个数据成员指向'D'元素,第一个数据成员指向'C'元素,作为构建过程的一部分。

所以,insert()和emplace()的主要区别在于emplace()是当场构造插入元素,而insert()必须复制或移动该元素。

这篇文章解释了什么是map-emplace以及如何使用map的两个主要的emplace成员函数。

文章内容

地图元素

下面是四种水果的名称和它们的外部颜色。

    banana => yellow
    passion fruit => purple
    watermelon => green
    grape => pink

一个地图由键/值对组成。在这个普通的列表中,水果的名字是键,而外部颜色的名字是值。然而,这是一个成对的列表,不是单独的值的列表,也不是单独的键的列表。它是一个键/值对的列表。一个地图的键是唯一的。

在代码中,一个键和它的值被编码为一个元素,称为一对。每个元素都会被一个迭代器所指向。所以,一个地图元素把一个键看作是一个值,而这个键又对应着另一个值。所以,一个地图元素需要两个值来创建一个元素;而不是上面介绍中所表达的一个。这两个值在一些代码中,称为一对。

地图元素的模板是。

    pair<const key_type, mapped_type>

第一个参数是用于键,它被表示为key_type。第二个参数是对应于键的值。它被表示为mapped_type,而不是value_type。value_type实际上是。

    pair<const key_type, mapped_type>

的元素模板。在数组中,索引是指向值的。在map中,迭代器是指向对的。一个对是一个值,它可以由结构的第二个数据成员持有,第三个数据成员指向下一个元素,它也有一个对作为值;第一个数据成员指向上一个元素,它的值是其他一些对。

在上面的水果/颜色列表中,第一对可以被编码为如下。

    {"banana", "yellow"}

"香蕉 "是键,"黄色 "是值,形成一个键/值对。两个值的整个短名单是一个value_type的值,可以由结构元素的中间数据成员持有。该结构的第一个数据成员将指向上一个元素,该结构的第三个数据成员将指向下一个元素。

现在,一对是一个对象,其键的成员名在前,其值的成员名在后。

下面的程序将上述水果/颜色的列表放入一个地图中。

    #include <iostream>
    #include <map>
    using namespace std;
   
    int main()
    {
        map<const char*, const char*> mp = {{"banana","yellow"}, {"passion fruit","purple"}, {"watermelon","green"}, {"grape","pink"}};

        for (map<const char*, const char*>::iterator it = mp.begin(); it != mp.end(); it++)
            cout << it->first << " => " << it->second << endl;

        return 0;
    }

输出结果是:

    banana => yellow
    passion fruit => purple
    watermelon => green
    grape => pink

注意,必须包括地图库。

异质对

一个配对不一定有一个对普通用户来说有意义的键和值。它也可以有一个对普通用户没有意义但对程序员有意义的键和值。作为一个对程序员有意义的键/值对的例子,键可能是一个迭代器,而值是一个bool类型。

a_uniq.emplace(args)

这里,a_uniq是地图的名称。args是键和值对,用逗号分开。该成员函数返回一个对,其第一个值是一个迭代器(value_type);第二个值是一个bool,表示插入(当场构建)是否成功(true表示成功)。返回的迭代器指向插入的元素。C++规范没有指出这个函数的插入应该在列表中的什么位置(或者在前面或后面)。下面的程序说明了该函数的使用。

    #include <iostream>
    #include <map>
    using namespace std;
   
    int main()
    {
        map<const char*, const char*> mp = {{"banana","yellow"}, {"passion fruit","purple"}, {"watermelon","green"}, {"grape","pink"}};

        pair<map<const char*, const char*>::iterator, bool> pr = mp.emplace("strawberry","red");

        cout << (pr.first)->first << " => " << (pr.first)->second << " : " << pr.second << endl;
        cout << endl;

        for (map<const char*, const char*>::iterator it = mp.begin(); it != mp.end(); it++)
            cout << it->first << " => " << it->second << endl;

        return 0;
    }

输出结果是:

    strawberry => red : 1

    strawberry => red
    banana => yellow
    passion fruit => purple
    watermelon => green
    grape => pink

第一个输出行中的1表示真。注意args是如何被编码为("strawberry", "red")。不要混淆value_type和mapped _type。

a.emplace_hint(p, args)

这里,'a'是地图的名称。这个成员函数与上面的类似,但是建议在地图列表中进行置换的位置。它是一个迭代器,p指向插入(移位)之前的元素,并在内存中靠近它。该函数返回一个迭代器,而不是一个对。迭代器指向新插入的元素(对)。下面的程序说明了这一点。

    #include <iostream>
    #include <map>
    using namespace std;
   
    int main()
    {
        map<const char*, const char*> mp = {{"banana","yellow"}, {"passion fruit","purple"}, {"watermelon","green"}, {"grape","pink"}};

        map<const char*, const char*>::iterator p = mp.end();
        p--;

        map<const char*, const char*>::iterator iter = mp.emplace_hint(p, "strawberry","red");

        cout << iter->first << " => " << iter->second << endl;
        cout << endl;

        for (map<const char*, const char*>::iterator it = mp.begin(); it != mp.end(); it++)
            cout << it->first << " => " << it->second << endl;

        return 0;
    }

输出结果是:

    strawberry => red

    strawberry => red
    banana => yellow
    passion fruit => purple
    watermelon => green
    grape => pink

结论

Emplace和Insert是相似的。Emplace在获得数值后,以某种方式在列表中原地构建其元素。另一方面,Insert将其元素从某处复制到列表中,或者将元素从某处移到列表中。

注意:一个地图通常是按照键值排序创建的。为了实现上述地图,使用字符串对象作为键,而不是常数点到字符。