c++有序关联容器中键类型的约束

  本文以map为例来解释有序关联容器中键类型的约束。

  map是键-值对的集合,可以理解为关联数组:

std::map<int, string> map1;
map1.insert(make_pair<int, string>(4, "linux"));
map1.insert(make_pair<int, string>(2, "c/c++"));
map1.insert(make_pair<int, string>(1, "ARM"));
map1.insert(make_pair<int, string>(3, "python"));
map1.insert(make_pair<int, string>(3, "java"));

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

  运行结果:
这里写图片描述

  由此可得以下两个结论:
  (1) map中的元素是以键的大小顺序排序的
  (2) map中不允许出现键值重复的元素
  然而这也是c++中所有有序关联容器的共性。

  因为map中元素要顺序存放,这也就意味着键需要具有相关的比较函数。默认情况下,标准库使用的是键类型定义的<操作符来实现键的比较的。所用的比较函数必须在键类型上实现严格弱排序(“小于”关系)。

class Number{
public:
    int num;
    Number(int i) : num(i) {}
};

int main(void)
{
    Number n1(1), n2(2), n3(3), n4(4);
    std::map<Number, string> map2;
    map2.insert(make_pair<Number, string>(n4, "linux"));
    map2.insert(make_pair<Number, string>(n2, "c/c++"));
    map2.insert(make_pair<Number, string>(n1, "ARM"));
    map2.insert(make_pair<Number, string>(n3, "python"));

    std::map<Number, string>::iterator it = map2.begin();
    while (it != map2.end())
    {
        cout << (it->first).num << ": " << it->second << endl;
        ++it;
    }

    return 0;
}

  如上代码片段自然是编译不过的:
这里写图片描述

  在Number中增加<操作符重载函数。
  解决办法1:在Number中增加<操作符重载函数。


尝试1:

class Number{
public:
    int num;
    Number(int i) : num(i) {}

    bool operator< (const Number& lNumber, const Number& rNumber)
    {
        return lNumber.num < rNumber.num;
    }
};

  编译:
这里写图片描述

  报错”must take exactly one argument”,这是因为以成员函数的方式重载运算符,不可改变该运算符原先的参数个数: 二元运算符用成员函数重载时,只需要一个参数,另一个参数由this指针传入

  将operator<()修改为:

bool operator< (const Number& _Number) 
{
    return this->num < _Number.num;
}

  编译:
这里写图片描述

  显然<操作符的左右操作数都要求是const的,然而在这里左操作数是非const(右操作数是const)的。所以要将该函数加上const属性:

bool operator< (const Number& _Number) const 
{
    return this->num < _Number.num;
}

  编译运行得到正确结果:
这里写图片描述


尝试2:
  二元运算符的重载函数若为非成员函数,就需要接收两个参数(左右操作数),若num是Number中的私有成员时,还需要在类中将该函数声明为友元函数:

class Number{
public:
    int num;
    Number(int i) : num(i) {}
    friend bool operator< (const Number& lNumber, const Number& rNumber);
};

bool operator< (const Number& lNumber, const Number& rNumber)
{
    return lNumber.num < rNumber.num;
}

  编译运行能同样的结果。

  解决办法2:将比较函数传入map
  以上是方法1,下来说到的方法–自定义函数并其将作为定义map时的第三个参数(一般定义map只需要键-值两个参数)也能实现。
  (1) 要将函数作为参数,自然想到用c语言中经常使用的函数指针:

class Number{
public:
    int num;
    Number(int i) : num(i) {}
};

bool m_comper(const Number& lNumber, const Number& rNumber)
{
    return lNumber.num < rNumber.num;
}

typedef bool (*pfunc)(const Number& lNumber, const Number& rNumber);

int main()
{
    Number n1(1), n2(2), n3(3), n4(4);
    std::map<Number, string, pfunc> map2(m_comper); //注意要将pfunc类型的函数实体为map2初始化,不然运行出现Segmentation fault
    map2.insert(make_pair<Number, string>(n4, "linux"));
    map2.insert(make_pair<Number, string>(n2, "c/c++"));
    map2.insert(make_pair<Number, string>(n1, "ARM"));
    map2.insert(make_pair<Number, string>(n3, "python"));

    std::map<Number, string>::iterator it = map2.begin();
    while (it != map2.end())
    {
        cout << (it->first).num << ": " << it->second << endl;
        ++it;
    }
    return 0;
}

  (2) 再者,可以使用c++中的函数调用符号()的重载函数,即定义类仿函数:

class Comp
{
public:
    bool operator() (const Number& lNumber, const Number& rNumber)
    {
        return lNumber.num < rNumber.num;
    }
};

int main(void)
{
    Number n1(1), n2(2), n3(3), n4(4);
    std::map<Number, string, Comp> map2;    //使用类仿函数不需要用Comp类的实体为map2初始化
    map2.insert(make_pair<Number, string>(n4, "linux"));
    map2.insert(make_pair<Number, string>(n2, "c/c++"));
    map2.insert(make_pair<Number, string>(n1, "ARM"));
    map2.insert(make_pair<Number, string>(n3, "python"));

    std::map<Number, string>::iterator it = map2.begin();
    while (it != map2.end())
    {
        cout << (it->first).num << ": " << it->second << endl;
        ++it;
    }

    return 0;
}

  注: 本文以map为例,但是(对键类型的约束条件)结论适用于c++中所有有序关联容器。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值