描述:
使用一个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失效问题,从而引发程序崩溃。