您的位置:首页 > 编程语言 > C语言/C++

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

2017-12-02 17:29 323 查看
  本文以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,
c21c
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++中所有有序关联容器。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: