细节改善效率:map::operator[]和map::insert具体做了什么

readme

我们从两个“灯下黑”的问题开始。
提问1:如何向一个map容器中添加一个元素,有什么区别?
是否有第三种方法本人不晓得,但是map::operator[]和map::insert是两种最常用的方法。它们都很常见,保证任何一个c++程序员都用过很多次,多到忽略了思考它们是否有区别。
提问2:如何更新一个map容器中的某个元素?
map::operator[]是最常用的方法,map::insert其实也可以,但是绕了一个弯。
这篇博客回答了这两个“灯下黑”的问题。

测试程序

//common.h
#ifndef _COMMON_H_
#define _COMMON_H_

#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <utility>
#include <algorithm>
#include <vector>
#include <iterator>
#include <sys/time.h>

using namespace std;
#endif
//insert_vs_index.cpp
#include "common.h"

class testclass
{
public:
    testclass();
    testclass(const double& other);
    testclass& operator=(const double& other);
    ~testclass() {};

    double get_value()
    {
        return m_testdouble;
    };

private:
    double m_testdouble;
};

testclass::testclass()
{
    printf("in testclass::testclass()\n");
    m_testdouble = 0;
}

testclass::testclass(const double& other)
{
    printf("in testclass(double testclass)\n");
    m_testdouble = other;
}

testclass& testclass::operator=(const double& other)
{
    printf("in testclass& testclass::operator=(double testvalue)\n");
    m_testdouble = other;
    return *this;
}

int main(void)
{
    {
        map<int, testclass> testmap;
        printf("operation start\n");
        printf("add element by index\n");
        testmap[1] = 100.1;
        printf("add element by insert\n");
        testmap.insert(make_pair(2, 100.2));
        printf("operation finish\n");
    }

    {
        map<int, testclass> testmap;
        printf("operation start\n");
        printf("map prepare start\n");
        testmap[1] = 100.1;
        testmap[2] = 100.2;
        printf("map prepare finish\n");
        printf("add element by index\n");
        testmap[1] = 100.3;
        printf("add element by insert\n");
        auto testpair = testmap.insert(make_pair(2, 100.4));
        if (!testpair.second)
        {
            printf("failed in testmap.insert(make_pair(2, 100.4));\n");
        }
        testpair.first->second = 100.4;
        printf("testmap[2]: %f\n", testmap[2].get_value());
        printf("operation finish\n");
    }

    return 0;
}

编译和执行结果

g++ -o insert_vs_index insert_vs_index.cpp -O0 -g -Wall -std=c++11
./insert_vs_index 
operation start
add element by index
in testclass::testclass()
in testclass& testclass::operator=(double testvalue)
add element by insert
in testclass(double testclass)
operation finish
operation start
map prepare start
in testclass::testclass()
in testclass& testclass::operator=(double testvalue)
in testclass::testclass()
in testclass& testclass::operator=(double testvalue)
map prepare finish
add element by index
in testclass& testclass::operator=(double testvalue)
add element by insert
failed in testmap.insert(make_pair(2, 100.4));
in testclass& testclass::operator=(double testvalue)
testmap[2]: 100.400000
operation finish

关于map::insert的多一点介绍

map::insert向map容器中添加元素,它有一个返回值。map::insert会返回一个pair<first, second>对象。如果insert的元素事先在map中不存在,first变量是一个指向插入后元素的迭代器;如果insert的元素事先map中已经存在,first变量是指向map中相同键值对应的元素的迭代器。至于second变量,代表insert操作是否成功,如果元素事先在map中不存在second变量为true,如果元素事先已经在map中存在second变量为false。

如何向一个map容器中添加一个元素,有什么区别?

map::operator[]和map::insert两种方法都可以实现向map容器中添加一个元素的目的。至于它们之间的区别,这里需要分类讨论:被添加的元素事先在map中不存在;被添加的元素事先在map中已存在。

被添加的元素事先在map中不存在

map::operator[]会在map中创建一个元素(k, v),其中v通过默认构造函数创建。map::operator[]返回v的引用,紧接着v被赋予为新值。听上去已经有点冗余了。
而map::insert高效很多,直接使用目标值构造一个v,不需要赋值操作。
结论就是如果被添加的元素事先在map中不存在,map::insert的效率要好于map::operator[]。

被添加的元素事先在map中存在

map::operator[]会返回一个引用,指向map中相同k对应的v,之后用目标值更新原来的v。
而map::insert就复杂了,如果元素事先已经在map中存在,map::insert会以失败告终,详见测试代码执行结果打印出来的“failed in testmap.insert(make_pair(2, 100.4));”。map::insert如果想要达到更新的目的,需要根据insert操作的结果作出进一步赋值操作。这里隐含着pair对象的构建和析构,是相较于map::operator[]多余的操作。
结论就是如果被添加元素事先在map中存在,map::operator[]的效率要好于map::insert。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
effective stl pdf 怎么使用stl 这里几乎都有说明 条款1: 仔细选择你要的容器 条款2: 小心对“容器无关代码”的幻想 条款3: 使容器里对象的拷贝操作轻量而正确 条款4: 用empty来代替检查size是否为0 条款5: 尽量使用范围成员函数代替他们的单元素兄弟 条款6: 警惕C++的及其令人恼怒的分析 条款7: 当使用new得指针的容器时,切记在容器销毁前delete那些指针 条款8: 千万不要把auto_ptr放入容器中 条款9: 小心选择删除选项 条款10: 当心allocator的协定和约束 条款11: 了解自定义allocator的正统使用法 条款12: 对STL容器的线程安全性的期待现实一些 vector和string 条款13: 尽量使用vector和string来代替动态申请的数组 条款14: 用reserve来避免不必要的内存重新分配 条款15: 当心string的实现中的变化 条款16: 如何将vector和string的数据传给传统的API 条款17: 用“交换技巧”来修正过度的容量 条款18: 避免使用vector<bool> 关联容器 条款19: 了解相等和等价的区别 条款20: 为包含指针的关联容器指定比较类型 条款21: 永远让比较函数对相等的值返回false 条款22: 避免对set和multiset的键值进行修改 条款23: 考虑用排序的vector代替关联容器 条款24: 当效率很关键时尽量用map::insert代替map::operator 条款25: 让自己熟悉非标准的hash容器 迭代器 条款26: 尽量使用iterator代替const_iterator,reverse_iterator和const_reverse_iterator 条款27: 使用distance和advance把const_iterators转化成iterators 条款28: 了解如何通过reverse_iterator的base得到iterator 条款29: 需要一字符一字符输入时请用istreambuf_iterator 算法 条款30: 确保目的范围足够大 条款31: 了解你的排序选项 条款32: 如果你真的想删除东西的话在remove-like的算法后紧接上erase 条款33: 当心在包含指针的容器使用remove-like的算法 条款34: 注意哪些算法需要排序过的范围 条款35: 通过mismatch或lexicographical_compare实现简单的忽略大小写字符串比较 条款36: 用not1和remove_copy_if来表现copy_if 条款37: 用accumulate或for_each来统计序列 仿函数,仿函数类,函数等等 条款38: 把仿函数类设计成值传递的 条款39: 用纯函数predicate 条款40: 增强仿函数类的适应性 条款41: 明确ptr_fun, mem_fun和mem_fun_ref的区别 条款42: 保证less是operator<的意思 用STL编程 条款43: 尽量用算法调用代替手写循环 条款44: 尽量用成员函数代替同名的算法 条款45: 注意count、find、binary_search、lower_bound、upper_bound和equal_range的区别 条款46: 考虑用函数对象代替函数作为算法的参数 条款47: 避免产生只写代码 条款48: 总是#include适当的头文件 条款49: 学会破解STL相关的编译器出错信息 条款50: 让自己熟悉STL相关的网站

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值