剖析STL map运算符重载[]——已经删除的元素是从哪里多出来的?

1 篇文章 0 订阅

1、怪异的现象

一次偶然的误用,竟然发现map中删除的元素,在后面又出现在了map中,如下代码所示

	map<int, A*> containers;
    A* a1 = new A();
    A* a2 = new A(2);
    A* a3 = new A(3);

    containers.insert(pair<int, A*>(1,a1));
    containers.insert(pair<int, A*>(2,a2));
    containers.insert(pair<int, A*>(3,a3));

    cout << "containers.size()=" << containers.size() << endl;

    // remove 2 from map
    containers.erase(2);
    cout << "---------------------------After erase--------------------------" << endl;
    cout << "containers.size()=" << containers.size() << endl;
    for (auto& m : containers) {
        cout << m.first << ":" << m.second->getB() << endl;
    }

    delete a1;
    a1 = nullptr;
    delete a2;
    a2 = nullptr;
    delete a3;
    a3 = nullptr;

    // []重载
    if (containers[2] == nullptr) {
        cout << "containers[2] is null" << endl;
    } else {
        cout << "containers[2] not null" << endl;
    }
	
	cout << "---------------------------After if--------------------------" << endl;
    cout << "containers.size()=" << containers.size() << endl;

功能很简单,就是从map中删除key为2的元素,释放完资源后判断了一下key为2的元素是否为nullptr,结果神奇的现象出现了,就是这里的判断导致map的大小又回到了删除前的大小。
在这里插入图片描述
看到这里 ,你是不是也一脸问号,明明删除了一个元素之后,map的大小为何又增加了?

2、错误使用map []运算符

还记得上面代码最后的if判断吗,罪魁祸首就是这个判断
在这里插入图片描述
这里本意是使用key去看map中对应的value值是否为空,结果却重新在map中添加了一个元素。这个地方当时和同事排查了两三天,gdb一步步调试,看到gdb显示的map大小从2变为3的时候,我们都懵了。压根没想到是map []运算符的问题,在错误的方向上找了很久…最后还是同事调试的时候跟进去了map的源码中,终于发现了罪魁祸首!!!下面的内容截自stl_map.h
在这里插入图片描述
map中保存的value是指针类型,默认初始值是nullptr,就导致if判断中又把删除的元素加进去了。

注意!!!这里如果要判断key为某个值的元素是否存在,一定不能在删除之后使用[],可以使用count(key)去判断
改为下面的判断就没有问题了

	if (containers.count(2) > 0) {
        cout << "containers[2] exist" << endl;
    } else {
        cout << "containers[2] not exist" << endl;
    }

在这里插入图片描述

3、总结

C++的STL很强大,需要注意的细节也有很多,就像这次不了解map的[]运算符会重新生成一个元素,导致走了很多冤枉路。特别在此记录,分享给更多的人,少踩坑!

4、附录-测试代码

A.h

#ifndef A_H
#define A_H

class A{
public:
    A();
    A(int _x);
    int getB();

private:
    int b{-1};
};


#endif //A_H

A.cpp

#include "A.h"

A::A() {

}

A::A(int _x) {
    b = _x;
}

int A::getB() {
    return b;
}

main.cpp

#include <iostream>
#include <map>
#include "A.h"

using namespace std;

int main(int argc, char *argv[])
{
    map<int, A*> containers;
    A* a1 = new A();
    A* a2 = new A(2);
    A* a3 = new A(3);

    containers.insert(pair<int, A*>(1,a1));
    containers.insert(pair<int, A*>(2,a2));
    containers.insert(pair<int, A*>(3,a3));

    cout << "containers.size()=" << containers.size() << endl;

    // remove 2 from map
    containers.erase(2);
    cout << "---------------------------After erase--------------------------" << endl;
    cout << "containers.size()=" << containers.size() << endl;
    for (auto& m : containers) {
        cout << m.first << ":" << m.second->getB() << endl;
    }

    delete a1;
    a1 = nullptr;
    delete a2;
    a2 = nullptr;
    delete a3;
    a3 = nullptr;

//    []重载会生成不存在的key-value,在删除之后不能使用这种方式判断
//    if (containers[2] == nullptr) {
//        cout << "containers[2] is null" << endl;
//    } else {
//        cout << "containers[2] not null" << endl;
//    }

    // count判断是否存在,ok
    if (containers.count(2) > 0) {
        cout << "containers[2] exist" << endl;
    } else {
        cout << "containers[2] not exist" << endl;
    }

    cout << "---------------------------After if--------------------------" << endl;
    cout << "containers.size()=" << containers.size() << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值