c++之map使用踩坑

描述:

       使用一个map<int,int> m; 可以使用对应的函数插入数据,因为map重载了[]运算符,所以可以通过m[key] = value进行赋值。一般情况,value为默认类型啊,直接使用[]运算符直接操作即可。没什么问题。

 当value为自定义类,会出现相关问题,例如:

#include <iostream>
#include <map>
#include <string>
#include <stdio.h>
using namespace std;

class A {

public:
  A(){
    cout<<"A()"<<",Line:"<<__LINE__<<endl;
  }
  A(int key,int val): key(key),val(val) {
    cout<<"A(int key,int val) = "<<"k="<<key<<"v="<<val<<",Line:"<<__LINE__<<endl;
  }
  A(const A & a) {
    cout<<"A(A & a) =,Line:"<<__LINE__<<endl;
    key = a.key;
    val = a.val;
  }
  const A & operator=(const A & a) { //返回引用可以改变左值。
    cout<<"A & operator=,Line:"<<__LINE__<<endl;
    key = a.key;
    val = a.val;
    return *this;
  }
  int key;
  int val;
};

int main() {
  std::map<int,A> vm = {{1,A(1,1)}}; 

  cout<<"Start vm"<<endl;
  // 使用[]时,先调用默认构造,再调用赋值函数,如果没有实现默认构造,编译会报错,错误如下<<...>>

  /*
   /usr/include/c++/8/tuple:1668:70: 错误:no matching function for call to ‘A::A()’
         second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
  */
  所示,或者使用insert_or_assign函数.
  vm[2] = A(2,2);   
  cout<<"End vm"<<endl;  

  return 0;
}
  /*
    A(int key,int val) = k=1v=1,Line:14
    A(A & a) =,Line:17
    A(A & a) =,Line:17
    Start vm
    A(int key,int val) = k=2v=2,Line:14
    A(),Line:11
    A & operator=,Line:22
    End vm
  */

    map中value是自定义类型的,直接使用[]进行存储结时,例中value的调用自身方法为先调用{默认无参构函数,再调用赋值函数}
    所以,如下使用[]存value,必须在student中实现默认构造,不然编译会出错,如下:
    << 
      没有默认无参构造函数,直接编译报错失败:(添加默认构造函即可解决该问题)
      /usr/include/c++/4.8.2/tuple:1090:70: error: no matching function for call to ‘A::A()’
      second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
    >>
    c++17中,解决了该问题,可以直接使用insert_or_assign()
    vm.insert_or_assign(0,A(0,0));

2.使用结构体或者类作为map的key是,需要重载运算符<,不然会就报错,(因为要排序存储,需要自定义对应的排序函数进行判别)

/usr/include/c++/5/bits/stl_function.h:387:20: error: no match for ‘operator<’ (operand types are ‘const Person’ and ‘const Person’)
       { return __x < __y; }

通常使用string直接坐为key,那是因为string重载了运算符<. 

正确使用代码:

#include <iostream>
#include <map>
#include <string>
#include <stdio.h>
using namespace std;

class A {

public:
  A(){
    cout<<"A()"<<",Line:"<<__LINE__<<endl;
  }
  A(int key,int val): key(key),val(val) {
    cout<<"A(int key,int val) = "<<"k="<<key<<"v="<<val<<",Line:"<<__LINE__<<endl;
  }
  A(const A & a) {
    cout<<"A(A & a) =,Line:"<<__LINE__<<endl;
    key = a.key;
    val = a.val;
  }

  
  const A & operator=(const A & a) {
    cout<<"A & operator=,Line:"<<__LINE__<<endl;
    key = a.key;
    val = a.val;
    return *this;
  }

  bool operator< (const A & a) const {  // 重载运算符,因为插入map是需要进行当前key值的比较排
    return key < a.key || val < a.val;
  }
  int key;
  int val;
};


void display(std::map<A,int> mm){
  for(const auto & iter:mm) {
    cout<<iter.first.key<<"--"<<iter.first.val<<"--"<<iter.second<<endl;
  }
}
int main() {

  std::map<A,int> sm;
  sm[A(4,5)] = 1;
  display(sm);

  return 0;
}
输出:

4--5--1
4--6--2

3.map之erase方法使用注意:

#include <iostream>
#include <map>
#include <vector>


int main()
{

    std::vector<std::string> v;
    v.push_back("one");
    v.push_back("two");
    v.push_back("three");

    for(std::vector<std::string>::iterator it = v.begin(); it != v.end(); )
    {
        std::cout << *it << std::endl;
        if(*it == "two")
            it = v.erase(it);
        else 
            ++it;

    }

    std::map<int, std::string> m;
    m.insert(std::make_pair(1, "one"));
    m.insert(std::make_pair(2, "two"));
    m.insert(std::make_pair(3, "three"));
    m.insert(std::make_pair(4, "four"));
    m.insert(std::make_pair(5, "five"));

    std::cout<< "before erase" << std::endl;
    for (std::map<int, std::string>::iterator it = m.begin(); it != m.end(); ++it)
    {
        std::cout << it->first << " " << it->second << std::endl;
    }

    for(std::map<int, std::string>::iterator it = m.begin(); it != m.end();)
    {
        if(it->first == 3)
        {
            m.erase(it); //该处会崩溃
            //m.erase(it++); // 正确用法
        }else
        {
            ++it;
        }
    }

    std::cout << "After erase" << std::endl;
    for (std::map<int, std::string>::iterator it = m.begin(); it != m.end(); ++it)
    {
        std::cout << it->first << " " << it->second << std::endl;
    }
    return 0;
}

上述问题:

       使用erase方法,通过查看源代码,再c++98中,如下图发现:

上两个图对比发现,不同版本实现的erase存在偏差,再98中,如果按照上代码实现,出现崩溃问题,后经过查询发现stl内部erase的实现,当调用时,会拷贝一份当前迭代器,之后如果没有讲it移动,那么当前的it就会失效,从而导致程序崩溃异常。正确实现做法:m.erase(it++).it++被当成参数传递,由于函数参数执行先于函数调用,所以,调用erase(it++)时,先拷贝一份it的值,再进行it++,之后再调用函数erase。此时it已经跳到下一个元素位置,erase从map中剔除目标元素。如果it没有++,那么就会造成剔除元素后造成it失效问题,从而引发程序崩溃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Steps-of-time

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值