map类提供了一个(排序的)关联数组。 在使用map容器之前,必须包含<map>头文件。
map用于填充了键值对,该键值对可以是任何容器接受的类型。 由于类型与键和值都相关联,因此必须在“<key,value>”符号中指定两种类型. key字段代表键的类型,value字段代表值的类型。 例如,可以如下定义其中key是string且,value是int类型的映射:
map<string,int>
键(key)用于访问它关联的值(value),例如,电话簿使用人的名字作为key,并使用电话号码以及可能的其他信息(例如,邮政编码,地址,职业)作为值.
由于map容器对键进行排序,因此必须定义键的operator <,并且必须谨慎地使用。例如,将指针作为key通常是一个坏主义,因为对指针进行排序与对这些指针所指向的值进行排序不同。 除了键,值类型外,第三个类型定义了比较类,用于比较两个键。 默认情况下,比较类是std :: less <KeyType>,使用键类型的operator <比较两个键值。 因此,对于键类型KeyType和值类型ValueType,映射的类型定义如下所示:
map<KeyType, ValueType, std::less<KeyType>>
使用类类型作为map容器的key
默认情况下,std :: map使用“ operator <”作为键的排序条件。 对于int和std :: string等默认数据类型,默认情况下,operator <是可用的,但对于用户定义的类,operator <默认情况下不可用。因此,要将用户定义的类对象用作std :: map中的键,我们应该选择其中一个:
- 默认排序标准,即为我们的class定义的operator < 。
- 应该为std::map分配外部排序条件,即可以比较用户定义类的两个对象的比较器。
下面我们用一个类似超市的结算小程序来作为本小节的示例,我们在Customer类中定义了一个map<Good, double> cart用于表示,消费者往购物车放入的商品(用Good类表示),以及对应商品的数量(用double表示)。
Customer类接口
#ifndef CUSTOMER_HH
#define CUSTOMER_HH
#include "../header/good.hh"
#include <iostream>
#include <map>
#include <string>
class Customer
{
std::string d_fullname; //用户名
double d_pay; //应付金额
double d_bonus; //奖励积分
double d_count; //购买数量
double d_discnt; //让利总计
std::map<Good, double> cart;
public:
Customer(std::string, double);
//购买
void buy(const Good, double);
//支付
void payment();
//结帐信息
void display_info();
private:
//折扣
void discount(const Good &, double);
};
#endif
Good类接口
#ifndef GOOD_HH
#define GOOD_HH
#include <string>
class Good
{
std::string d_name;
double d_price;
public:
Good();
Good(std::string, double);
double price() const;
void set_price(double price);
std::string name() const;
bool operator<(const Good &obj) const;
};
#endif
方案1:重载operator <用于key的排序
示例中的Good类是具有d_price属性,类Customer的属性cart,它正是std :: map容器,我们将Good类作为cart中的key。我们将在Good类中重写将重载 operator <。如下实现所示
bool Good::operator<(const Good &obj) const
{
if (obj.d_price < this->d_price)
{
return true;
}
}
Customer接口实现
#include "../header/customer.hh"
Customer::Customer(std::string user, double bonus)
{
d_bonus = bonus;
d_fullname = user;
}
void Customer::buy(const Good com, double count)
{
cart[com] = count;
}
void Customer::discount(const Good &com, double count)
{
double curPay = com.price() * count;
double curDis;
if (d_bonus < 400)
{
curDis = curPay * 0;
}
else if (d_bonus <= 500)
{
curDis = curPay * 0.03;
}
else if (d_bonus <= 500)
{
curDis = curPay * 0.04;
}
else if (d_bonus <= 600)
{
curDis = curPay * 0.05;
}
else if (d_bonus <= 700)
{
curDis = curPay * 0.06;
}
else if (d_bonus <= 800)
{
curDis = curPay * 0.07;
}
d_discnt += curDis;
d_pay += curPay;
}
void Customer::payment()
{
std::map<Good, double>::iterator be = cart.begin();
std::map<Good, double>::iterator ed = cart.end();
while (be != ed)
{
discount(be->first, be->second);
be++;
}
}
void Customer::display_info()
{
std::map<Good, double>::iterator be = cart.begin();
std::map<Good, double>::iterator ed = cart.end();
std::cout << std::endl;
std::cout << "客户名:" << d_fullname << std::endl;
std::cout << "品名"
<< "\t"
<< "单价"
<< "\t"
<< "数量"
<< "\t"
<< "小结" << std::endl;
Good c;
std::string name;
double price;
double count;
while (be != ed)
{
c = be->first;
name = c.name();
price = c.price();
count = be->second;
std::cout << name << "\t"
<< price << "\t"
<< count << "\t"
<< price * count << std::endl;
be++;
}
std::cout << "金额:" << d_pay << "RMB" << std::endl;
std::cout << "优惠:" << d_discnt << "RMB" << std::endl;
std::cout << "应付" << d_pay - d_discnt << "RMB" << std::endl;
std::cout << std::endl;
}
Good类接口的实现
#include "../header/good.hh"
Good::Good(){};
Good::Good(std::string name, double price)
{
d_name = name;
d_price = price;
}
void Good::set_price(double value)
{
d_price = value;
}
double Good::price() const
{
return d_price;
}
bool Good::operator<(const Good &obj) const
{
if (obj.d_price < this->d_price)
{
return true;
}
}
std::string Good::name() const
{
return d_name;
}
调用示例
#include "./customer.cpp"
#include "./good.cpp"
int main(int argc, char const *argv[])
{
Good a{"香蕉", 5.7};
Good b{"奇异果", 13.4};
Good c{"榴莲", 13.5};
Good d{"苹果", 5.7};
Customer p1{"Lisa", 400};
Customer p2{"Mary", 501};
Customer p3{"Chey", 600};
Customer p4{"May", 800};
p1.buy(a, 23);
p1.buy(c, 12);
p1.buy(d, 32);
p2.buy(c, 34);
p2.buy(a, 113);
p2.buy(b, 17);
p1.payment();
p2.payment();
p1.display_info();
p2.display_info();
return 0;
}
程序输出
![](https://img.haomeiwen.com/i16148197/71cc61a4ec26f56c.png)
正如,我们在map容器中指定键中的类类型的operator <的数据成员比较细节,可以让程序正常输出。另外,从这个示例,可以知道map容器在日常的分类统计应用开发中,能够用简洁的调用代码就能实现。
方案2:
现在我们要更改键的排序标准,即针对Good的d_name属性,而不是d_price属性。我们这次重写bool operator(const T &left,const T &right) const,即
bool Good::operator(const Good &c1,const Good &c2) const
{
return (c1.name()>c2.name());
}
只要将方案1的示例代码中的实现,替换为方案2中的比较器bool operator(const T &left,const T &right) const的具体实现,也可以令程序正常运行,这里就不重复展示了。
Operator[]操作
operator[]默认在“查找/创建”模式下运行,即调用键时,它将尝试查找具有给定键key的元素,并且可以在以下两个方式进行操作,即
- 如果找到任何带有该键的元素,则它将返回其值的引用。如上面的示例代码。由于返回的是一个值的引用,而引用是一个左值,因此也可通过该“[键]”的形式去修改对应键上的值。
void Customer::buy(const Good com, double count) { cart[com] = count; }
- 如果map中没有找到该键,则它将在map使用该键创建一个新元素,并在其value字段中分配value_type的默认值。 然后它将返回新创建的元素值的引用。
基于value字段的查找操作
默认情况下,map容器的[]操作符号可能无法满足我们的需求,那么基于值的查找需求,我们就需要自行实现这个查找算法。
我们通过传递一个vector对象的引用给search_by_value()函数,将符合value参数的对应结果存放到vector容器中。另外,这个算法使用了模板技术可以应用于所有用户定义的类型。
#include <vector>
template <typename K, typename V>
bool search_by_value(std::vector<K> &vc, std::map<K, V> ct, V value)
{
bool isFind = false;
auto it = ct.begin();
auto ed = ct.end();
while (it != ed)
{
if (it->second == value)
{
isFind = true;
vc.push_back(it->first);
}
}
return isFind;
}
基于key字段的查找
上面基于operator[]的操作虽然有查找的功能,但它在没有找到对应key字段的值,会自动创建一个对应查找key字段中对象的默认值,通常这不是我们需要的结果,那么,我们应该使用map::find函数。这个find函数很简单,我就不复述这些应用示例了。
网友评论