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

目录

  • 1、怪异的现象
  • 2、错误使用map []运算符
  • 3、总结
  • 4、附录-测试代码

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的大小又回到了删除前的大小。
剖析STL map运算符重载[]——已经删除的元素是从哪里多出来的?_第1张图片
看到这里 ,你是不是也一脸问号,明明删除了一个元素之后,map的大小为何又增加了?

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

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

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

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

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

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 
#include 
#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;
}

你可能感兴趣的:(C/C++,STL,c++,STL,map)