前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >STL之关联式容器map(二)

STL之关联式容器map(二)

作者头像
用户9831583
发布2022-06-16 14:41:09
5490
发布2022-06-16 14:41:09
举报
文章被收录于专栏:码出名企路

本文续:STL之关联式容器map(一)

3构造元素

emplace() 可以在适当的位置直接构造新元素,从而避免复制和移动操作。

当容器中现有元素的键与这个元素的键不同时,才会构造这个元素。

代码语言:javascript
复制
std::map<Name, size_t> people;
auto pr = people.emplace (Name { "Dan","Druff"},77);

成员函数 emplace() 和 insert() 返回的 pair 对象提供的指示相同。pair 的成员变量 first 是一个指向插入元素或阻止插入的元素的迭代器;成员变量 second 是个布尔值,如果元素插入成功,second 就为 true。

emplace_hint() 和 emplace() 生成元素的方式在本质上是一样的,除了必须为前者提供一个指示元素生成位置的迭代器,作为 emplace_hint() 的第一个参数。

代码语言:javascript
复制
std::map<Name, size_t> people;
auto pr = people.emplace(Name{"Dan","Druff"}, 77);
auto iter = people.emplace_hint (pr.first, Name {"Cal","Cutta"}, 62);

如果容器使用这个提示符,那么新元素会在这个指示符表示的位置之前生成,并尽可能靠近这个位置。

emplace_hint() 的返回值不是一个 pair 对象,如果新元素被插入,它返回的是指向新元素的迭代器;如果没有插入,返回的是和这个键匹配的现有元素的迭代器。

用 size() 成员函数来获取 map 中对应元素的数量来检查 map 元素增加的数量。

代码语言:javascript
复制
auto pr = people.emplace(Name{"Dan", "Druff"}, 77);
auto count = people.size ();
auto iter = people.emplace_hint (pr.first, Name {"Cal", "Cutta"}, 62);
if(count < people.size ())
std::cout <<"Success!\n";
4.获取元素

获取 map 容器的开始和结束迭代器以及反向迭代器,它们都可以访问容器中的所有元素。

map 的成员函数 at() 返回的是参数键对应的对象。如果这个键不存在,就会拋出 out_of_range 异常

代码语言:javascript
复制
Name key;
try{  
key = Name {"Dan”, ”Druff"};  
auto value = people.at(key);
std:: cout << key << "is aged " << value << std:: endl;  
key = Name {"Don", "Druff"};  
value = people.at(key);  
std::cout << key << " is aged " << value << std::endl;}
catch(const std::out_of_range& e){  
std::cerr << e.what() << '\n'<< key << " was not found." <<std::endl;}

Dan Druff is aged 77

invalid map<K, T> key Don Druff was not found.

当 catch 代码块中的代码执行后,try 代码块中的所有变量会被销毁,因此不再可以访问。

元素默认的构造函数会用键和键所关联的对象生成一个新元素,如果键关联的对象是基本数据类型,它的值为 0。

代码语言:javascript
复制
auto value = people[Name {"Ned", "Kelly"}]; // Creates a new element if the key is not there

因为容器中不存在这个键,所以用它生成了新元素。关联对象的值是 0,并会返回这个值。修改已存在的元素:

代码语言:javascript
复制
people[Name {"Ned", "Kelly”}] = 39; // Sets the value associated with the key to 39

综合应用:

假设通过人名来保存并检索名人名言。显然,一个名人会有很多名言,因此需要通过单个键来保存多个名言。不能在 map 容器中保存重复的键,但是可以将键关联到封装了多个名言的对象上。

代码语言:javascript
复制
    #ifndef QUOTATIONS_H
    #define QUOTATIONS_H

    #include <vector>                                          
     #include <string>                                        
       #include <exception>                                      
    
    class Quotations
    {
        private:
            std::vector<std::string> quotes;
        public:
            // 这里用 << 运算符来添加名言是合理的,它可以在其他一些场景下使用,例如输入流
            //第一个版本接收一个字符串常量参数,然后把它传给 vector 的成员函数 emplace_back(),
            //emplace__back() 会调用 string 的构造函数以在适当的位置生成元素
            Quotations& operator<<(const char* quote)
            {
                quotes.emplace_back(quote);
                return *this;
            }
            // 第二个版本只有一个参数,它是 string 对象的引用,这个参数会被传给 vector 的成员函数 push_back()
            Quotations& operator<<(const std::string& quote)
            {
                quotes.push_back(quote);
                return *this;
            }
            // 第三个版本有一个右值引用参数。当在函数体中通过名称使用右值引用时,它会变成左值,因此必须使用 move()
            //函数将它变为右值,然后把它传给 vector 的成员函数 push_back()。这会保证对象总是移动传值,而不是复制传值。
            Quotations& operator<<(std::string&& quote)
            {
                quotes.push_back(std::move(quote));
                return *this;
            }
            // 类的成员函数 []() 可以通过索引来访问成员元素。当索引不在范围内时,这个函数将抛出一个异常,
            //这种情况不应该发生;如果真的发生,这会是程序中的一个 bug。
            std::string& operator[](size_t index)
            {
                if(index < quotes.size())
                    return quotes[index];
                else
                    throw std::out_of_range {"Invalid index to quotations."};
            }
            size_t size() const// Returns the number of quotations
            {
                return quotes.size();
            }
            // Returns the begin iterator for the quotations
            std::vector<std::string>::iterator begin()
            {
                return std::begin(quotes);
            }
            // Returns the const begin iterator for the quotations
            std::vector<std::string>::const_iterator begin() const
            {
                return std::begin(quotes);
            }
            // Returns the end iterator for the quotations
            std::vector<std::string>::iterator end()
            {
                return std::end(quotes);
            }
            // Returns the const end iterator for the quotations
            std::vector<std::string>::const_iterator end() const
            {
                return std::end(quotes);
            }
    };
    #endif

如果没有定义 const 版的 begin() 和 end() 函数,就不能在 for 循环中使用 const 类型的循环变量:

代码语言:javascript
复制
for (const auto& pr : quotations)//Requires const iterators    ...
代码语言:javascript
复制
   #include <iostream>                              
    #include <cctype>                                // For toupper()
    #include <map>                                  
    #include <string>                              
    #include "Quotations.h"
    #include "name.h"
    using std::string;
    // 第一个用来从 cin 读入 name:
    inline Name get_name()
    {
        Name name {};
        std::cout << "Enter first name and second name: ";
        //std::ws 消除空格
        std::cin >> std::ws >> name;
        return name;
    }
    // 第二个辅助函数用来读取名言:
    inline string get_quote(const Name& name)
    {
        std::cout << "Enter the quotation for " << name
        << ". Enter * to end:\n";
        string quote;
        std::getline(std::cin >> std::ws, quote, '*');
        return quote;
    }
    int main()
    {
        std::map<Name, Quotations> quotations; //类,类
        std::cout << "Enter 'A' to add a quote."
        "\nEnter 'L' to list all quotes."
        "\nEnter 'G' to get a quote."
        "\nEnter 'Q' to end.\n";
        Name name {};                                  // Stores a name
        string quote {};                               // Stores a quotation
        char  command {};                              // Stores a command
        while(command != 'Q')
        {
            std::cout << "\nEnter command: ";
            std::cin >> command;
            command = static_cast<char>(std::toupper(command));
            switch(command)
            {
                case 'Q':
                    break;        
                case 'A':
                    name = get_name();
                    quote = get_quote(name);
                    //<< 左边的操作数等同于 quotations.operator[](name),它返回一个和 name 关联的 Quotations 对象
                    //quotations.operator[](name).operator<<(quote);
                    quotations[name] << quote;
                    break;
                case 'G':
                {
                    name = get_name();
                    const auto& quotes = quotations[name];
                    size_t count = quotes.size();
                    if(!count)
                    {
                        std::cout << "There are no quotes recorded for "<< name << std::endl;
                        continue;
                    }
                    size_t index {};
                    if(count > 1)
                    {
                        std::cout << "There are " << count << " quotes for " << name << ".\n"<< "Enter an index from 0 to " << count - 1 << ": ";
                        std::cin >> index;
                    }
                    //利用表达式 quotations[name][index] 来得到一条名言,
                    //它等价于 quotations.operator[](name).operator[](index)
                    std::cout << quotations[name][index] << std::endl;
                }
                break;
                case 'L':
                if(quotations.empty())                                        
                {
                    std::cout << "\nNo quotations recorded for anyone." << std::endl;
                }
                // List all quotations
                for(const auto& pr : quotations)                              
                {
                    std::cout << '\n' << pr.first << std::endl;
                    for(const auto& quote : pr.second)                          
                    {
                        std::cout << "  " << quote << std::endl;
                    }
                }
                break;
                default:
                    std::cout << " Command must be 'A', 'G', 'L', or 'Q'. Try again.\n";
                    continue;
                    break;
            }
        }
    }

结果显示:

5查找元素

map 容器的成员函数 find() 可以返回一个元素的迭代器,这个元素的键值和参数匹配。

代码语言:javascript
复制
std::map<std::string, size_t> people {{"Fred", 45}, {"Joan", 33}, {"Jill", 22}};
std::string name{"Joan"};
auto iter = people.find(name);
if(iter == std::end(people))  
std:: cout <<"Not found.\n";
else  
std:: cout << name << " is ""<< iter->second << std::endl;

如果没有和参数匹配的元素,find()函数会返回容器的结束迭代器,因此在使用这个迭代器之前,必须先对它进行检查。

6删除元素

map 的成员函数 erase() 可以移除键和参数匹配的元素,然后返回所移除元素的个数

代码语言:javascript
复制
std::map<std::string, size_t> people {{ "Fred", 45}, {"Joan", 33},{"Jill", 22}};
std::string name{"Joan"};
if(people.erase(name))  
std::cout << name << " was removed." << std::endl;
else  
std::cout << name << " was not found" << std::endl;

map 容器的返回值只可能是 0 或 1,0 表明元素不在容器中

也可以用指向删除元素的迭代器作为 erase() 的参数。这种情况下,返回的迭代器指向被删除元素的下一个位置。这个参数必须是容器中的有效迭代器,不能是结束迭代器。如果迭代器参数指向的是容器的最后一个元素,那么会返回结束迭代器。

代码语言:javascript
复制
auto iter = people.erase(std::begin(people));
if(iter == std::end(people))    
std::cout << "The last element was removed."<< std::endl;
else    
std::cout << "The element preceding " << iter->first << "was removed." << std::endl;

高级版本的 erase(),它可以移除两个迭代器参数所定义范围内的元素。

代码语言:javascript
复制
auto iter = people.erase(++std::begin(people), --std::end(people));//Erase all except 1st & last

返回的迭代器指向这段元素中最后一个被删除的元素

如果想删除容器中的所有元素,可以调用成员函数 clear()

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码出名企路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 3构造元素
  • 4.获取元素
  • 6删除元素
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档