<1> c++ 笔记 stl::map
目录
优先查看官网 cppreference.com
map 种类
1. 插入,添加元素
1.1 insert
1.2 emplace添加 (c++11)
2.0 批量拷贝,拼合
3.0 访问
3.1 at 访问
3.2 下标访问
4. 查找
4.1迭代器查找
4.2 contains查找 (c++20)
4.3 count查找
5. 删除
6. 遍历
7. 析构
testDemo:
8. emplace 和 insert
优先查看官网 cppreference.com
map笔记
这里只是记录常用方法,以及部分理解,方便自己查阅。以下demo皆以默认的c++11 标准。
map 种类
std::map为有序的,以key作为排序。 一对一的关系,一个key只一个元素,插入新的会覆盖之前
std::unordered_map :也可以使用std::unordered_map,不会排序。
std::mutimap 和 std::unordered_mutimap. 可以一对多,运行key重复。
1. 插入,添加元素
1.1 insert
std::map 将会有序插入
mDogs.insert(std::pair<std::string, std::shared_ptr<Dog>>(dog1->name(), dog1));
1.2 emplace添加 (c++11)
emplace 的意思是 安放,不至于再出现一次从元素原来的值在生存一个拷贝,将拷贝insert到容器的情况。
https://zh.cppreference.com/w/cpp/container/map/emplace
mDogs.emplace(std::make_pair(dog3->name(), dog3));
2.0 批量拷贝,拼合
mDogs2.insert(mDogs.begin(), mDogs.end());
3.0 访问
3.1 at 访问
如果没有元素,会抛出异常。
try
{
std::cout << "at 访问konglong" << std::endl;
mDogs.at("konglong")->talk();
}
catch (const std::exception &e)
{
std::cerr << "at 异常:no konglong find, " << e.what() << '\n'
<< '\n';
}
3.2 下标访问
如果不存在就会自动添加一个元素,不推荐使用
auto dog = mDogs["konglong"];
if (dog != nullptr)
{
dog->talk();
}
else
{
std::cout << "下标访问不存在的元素导致自动添加空数据!" << std::endl
<< std::endl;
}
4. 查找
map中,contian() 和 count() 内部都是使用迭代器find 的查找方式实现的。 mutimap的count()有所不同。
两外使用 std::find() 和std::find_if() 都不是map自身提供的方法,它是算法库里面提供的一种通用的泛形遍历查找的方法,本质上是一个通用的遍历比较函数。
4.1迭代器查找
std::map<std::string, std::shared_ptr<Dog>>::iterator it = mDogs.find("dahuang");
if (it != mDogs.end())
{
std::cout << "find查找: " << it->second->name() << std::endl
<< std::endl;
}
4.2 contains查找 (c++20)
// 需要 c++20才支持
#if __cplusplus > 201703L //需要c++20 才支持 编译的时候加上:g++ -std=c++20
std::cout << "contains 查找:" << std::endl;
if (mDogs.contains("dahuang"))
{
mDogs["dahuang"]->talk();
}
std::cout << std::endl;
#endif
4.3 count查找
if (mDogs.count("dahuang") != 0)
{
mDogs["dahuang"]->talk();
}
5. 删除
mDogs.erase("dahuang");
6. 遍历
推荐使用for 范围遍历:
for (auto &dog : dogs)
{ // 6.0 for 范围 for循环 c++11 起支持。//https://zh.cppreference.com/w/cpp/language/range-for
// 如果要修改元素,用auto &
// 否则也可以用 auto
if (dog.second != nullptr)
dog.second->talk();
else
std::cout << "a died dog: " << dog.first << std::endl;
}
7. 析构
要不要手动遍历逐个删除?要不要自己clear?
不需要。 元素自身会被删除。
// 析构函数只会删除元素,(编译器的工作)。如果元素是裸指针,被程序员赋予了一个内存,那就需要程序员自己去释放这个内存。(程序员自己的工作)
// 本篇这里用的是智能指针对象,元素具有在被删除的时候自动释放内存的能力,OK。
// #if __cplusplus >= 201103L
// /**
// * The dtor only erases the elements, and note that if the elements
// * themselves are pointers, the pointed-to memory is not touched in any
// * way. Managing the pointer is the user's responsibility.
// */
// ~map() = default;
// #endif
析构demo
#include <map>
#include <iostream>
#include <string>
class Dog
{
public:
Dog(std::string name, int color) : mName(name), mColor(color), bCopy(false)
{
std::cout << "new dog:" << mName << std::endl;
}
Dog(const Dog &dog) : mName(dog.mName + "_copy"), mColor(dog.mColor), bCopy(true)
{
std::cout << " 拷贝的dog :" << mName << std::endl;
}
~Dog()
{
std::cout << mName << " died!!" << std::endl;
}
private:
std::string mName;
int mColor = 0;
bool bCopy = false;
};
int main()
{
{
std::map<int, Dog *> dogs1;
dogs1.emplace(std::make_pair(1, new Dog("dahang1", 1)));
dogs1.emplace(std::make_pair(2, new Dog("xiaohei1", 2)));
//野指针。 Dog没有被释放
std::cout<<"map1 即将析构"<<std::endl;
}
std::cout<<"map1 已经析构"<<std::endl<<std::endl;
{
std::map<int, Dog> dogs2;
dogs2.insert(std::make_pair(1,Dog("dahuang2",1)));
dogs2.insert(std::make_pair(2,Dog("xiaohei2",2)));
std::cout<<"map2 即将析构-----"<<std::endl;
}
std::cout<<"map2 已经析构------"<<std::endl;
return 0;
}
可以看到,普通的指针不会被析构(dogs1.emplace(std::make_pair(1, new Dog("dahang1", 1))); 在c++中几乎没有这种写法,只有java才比较常用),而存储的对象,会被析构,而智能指针其本质也是一个对象,就会被析构。
testDemo:
#include <string>
#include <memory>
#include <iostream>
#include <map>
#include <unordered_map>
class Dog
{
public:
Dog(std::string name, int color) : mName(name), mColor(color)
{
}
~Dog()
{
std::cout << mName << " died!!" << std::endl;
}
void talk()
{
std::cout << mName << " ::wang wang" << std::endl;
}
std::string name()
{
return mName;
}
private:
std::string mName;
int mColor = 0;
};
int main(int argc, const char *argv[])
{
{
std::map<std::string, std::shared_ptr<Dog>> mDogs;
std::unordered_map<std::string, std::shared_ptr<Dog>> mDogs2;
auto dogsTalk = [=](std::map<std::string, std::shared_ptr<Dog>> dogs)
{
std::cout << "<<<<---------" << std::endl;
for (auto &dog : dogs)
{
if (dog.second != nullptr)
dog.second->talk();
else
std::cout << "a died dog:" << dog.first << std::endl;
}
std::cout << "--------->>>" << std::endl;
std::cout << std::endl;
};
auto dogsTalk2 = [=](std::unordered_map<std::string, std::shared_ptr<Dog>> dogs)
{
std::cout << "<<<---------" << std::endl;
for (auto &dog : dogs)
{ // 6.0 for 范围 for循环 c++11 起支持。//https://zh.cppreference.com/w/cpp/language/range-for
// 如果要修改元素,用auto &
// 否则也可以用 auto
if (dog.second != nullptr)
dog.second->talk();
else
std::cout << "a died dog: " << dog.first << std::endl;
}
std::cout << "--------->>>" << std::endl;
std::cout << std::endl;
};
{ //
std::shared_ptr<Dog> dog1 = std::make_shared<Dog>("dahuang", 2);
std::shared_ptr<Dog> dog2 = std::make_shared<Dog>("xiaohei", 3);
std::shared_ptr<Dog> dog3 = std::make_shared<Dog>("laobai", 4);
// 1.0 插入
mDogs.insert(std::pair<std::string, std::shared_ptr<Dog>>(dog1->name(), dog1));
mDogs.insert(std::pair<std::string, std::shared_ptr<Dog>>(dog2->name(), dog2));
// 1.1 emplace添加
// emplace 的意思是 安放,不至于再出现一次从元素原来的值在生存一个拷贝,将拷贝insert到容器的情况。
// https://zh.cppreference.com/w/cpp/container/map/emplace
mDogs.emplace(std::make_pair(dog3->name(), dog3));
std::cout << "插入完成,mDogs:" << std::endl;
dogsTalk(mDogs);
// 2.0 批量拷贝,拼合
mDogs2.insert(mDogs.begin(), mDogs.end());
std::cout << "拷贝完成,mDogs2:" << std::endl;
dogsTalk2(mDogs2);
// 3.0 at 访问
try
{
std::cout << "at 访问konglong" << std::endl;
mDogs.at("konglong")->talk();
}
catch (const std::exception &e)
{
std::cerr << "at 异常:no konglong find, " << e.what() << '\n'
<< '\n';
}
// 3.1 下标访问,如果不存在就会自动添加一个元素,不推荐使用
std::cout << "下标访问存在的元素:" << std::endl;
mDogs["dahuang"]->talk();
auto dog = mDogs["konglong"];
if (dog != nullptr)
{
dog->talk();
}
else
{
std::cout << "下标访问不存在的元素导致自动添加空数据!" << std::endl
<< std::endl;
}
// 4.0 查找
std::map<std::string, std::shared_ptr<Dog>>::iterator it = mDogs.find("dahuang");
if (it != mDogs.end())
{
std::cout << "find查找: " << it->second->name() << std::endl
<< std::endl;
}
// 4.1 contains查找
#if __cplusplus > 201703L //需要c++20 才支持 编译的时候加上:g++ -std=c++20
std::cout << "contains 查找:" << std::endl;
if (mDogs.contains("dahuang"))
{
mDogs["dahuang"]->talk();
}
std::cout << std::endl;
#endif
// 4.2 count查找
std::cout << "count 查找" << std::endl;
if (mDogs.count("dahuang") != 0)
{
mDogs["dahuang"]->talk();
}
std::cout << std::endl;
#if __cplusplus > 201103L
// 如果是 multimaps (支持一个key对应多个 value)
// c++11 以上 有 auto count
#endif
// 5.0 删除
mDogs.erase("dahuang");
std::cout << "删除之后:" << std::endl;
dogsTalk(mDogs);
std::cout << "元素创建括号即将结束" << std::endl;
}
std::cout << "元素创建括号结束" << std::endl
<< std::endl;
std::cout << "map创建括号即将结束,map即将被销毁" << std::endl;
}
// 析构函数只会删除元素,(编译器的工作)。如果元素是裸指针,被程序员赋予了一个内存,那就需要程序员自己去释放这个内存。(程序员自己的工作)
// 本篇这里用的是智能指针对象,元素具有在被删除的时候自动释放内存的能力,OK。
// #if __cplusplus >= 201103L
// /**
// * The dtor only erases the elements, and note that if the elements
// * themselves are pointers, the pointed-to memory is not touched in any
// * way. Managing the pointer is the user's responsibility.
// */
// ~map() = default;
// #endif
std::cout << "map创建括号结束,map已经被销毁" << std::endl;
}
g++ -std=c++20 stl_map.cpp
8. emplace 和 insert
实际测试发现,只是在 如下情况下,emplace才有比insert少一次拷贝构造的情况。
dogs2.emplace(3,Dog("laobai2",3));//只被拷贝一次
dogs2.insert(std::make_pair(4,Dog("ahuang",4)));//拷贝两次
// dogs2.insert(4,Dog("ahuang",4)); // 编译错误
测试demo:
#include <map>
#include <iostream>
#include <string>
class Dog
{
public:
Dog(std::string name, int color) : mName(name), mColor(color), bCopy(false)
{
std::cout << "new dog:" << mName << std::endl;
}
//实现一个拷贝构造函数,让被拷贝的dog对象,name加上一个后缀。
Dog(const Dog &dog) : mName(dog.mName + "_copy"), mColor(dog.mColor), bCopy(true)
{
std::cout << " 拷贝的dog :" << mName << std::endl;
}
~Dog()
{
std::cout << mName << " died!!" << std::endl;
}
private:
std::string mName;
int mColor = 0;
bool bCopy = false;
};
int main()
{
#if 1
{
std::map<int, Dog> dogs2;
{
Dog d("dahuang2", 1);
dogs2.emplace(std::make_pair(1, d)); // 拷贝两次。这里被拷贝复制。 为什么emplace 还拷贝?make_pair拷贝一次,emplace又拷贝一次。
std::cout << "empalce 添加 dahuang2" << std::endl;
}
{
dogs2.emplace(std::make_pair(2, Dog("xiaohei2", 2)));
std::cout << "empalce 添加 xiaohei2" << std::endl;
}
dogs2.emplace(3, Dog("laobai2", 3)); //只被拷贝一次
dogs2.insert(std::make_pair(4, Dog("ahuang", 4))); //拷贝两次
// dogs2.insert(4,Dog("ahuang",4)); // 编译错误
// dogs2.emplace(4,"cuihua",4); // 编译错误
// dogs2.emplace(4,("cuihua",4)); // 编译错误
std::cout << "map 即将销毁" << std::endl;
}
#endif
return 0;
}
可以看到:
- dogs2.emplace(std::make_pair(2, Dog("xiaohei2", 2)));// 拷贝两次。make_pair拷贝一次生成pair,emplace又拷贝一次放到map
- dogs2.emplace(3, Dog("laobai2", 3)); //只被拷贝一次
-
dogs2.insert(std::make_pair(4, Dog("ahuang", 4))); // 拷贝两次