1 set和multiset区别
区别:
- set不可以插入重复数据,而multiset可以
- set插入数据的同时会返回插入结果,表示插入是否成功
- multiset不会检测数据,因此可以插入重复数据
set.insert() set不能有重复值插入
在上一篇学习笔记知道set容器是不允许有重复值插入的,那么具体看看其中的原因。
首先,创建set容器,插入数据,选中insert→右键→转到定义,查看insert的定义
解释:
可以看到insert的返回值是pair类型,即返回值是对组类型,相当于返回值有两个数据,一个是迭代器iterator,表示插入值的位置,另一个是bool类型,表示这个值是否插入成功。对于set的insert而言,返回结果就是这个值是否插入成功,插入成功的话,也会有插入位置。其中,pair就是对组,是一组成对出现的数据。
multiset.insert() 允许有重复值插入
同样地,看看multiset容器中insert()函数是怎么定义的。
首先,创建set容器,插入数据,选中insert→右键→转到定义,查看insert的定义
解释:
在multiset容器中insert的返回值是一个迭代器,没有bool类型的返回值来判断是否重复插入,不像set容器中insert的返回值是pair对组类型。
接下来看看set容器和multiset容器重复插入一个值,insert的返回结果会有什么不一样。
测试代码:
void test1()
{
int n = 30;
cout << "set.insert() " << endl;
set<int> s1;
//insert的返回值类型是pair对组类型(成对出现的数据,数据1是迭代器,数据2是bool类型)
pair<set<int>::iterator, bool> result1 = s1.insert(21);
if (result1.second)//第二个数据是bool类型
{
cout << "第一次插入成功" << endl;//返回真 插入成功
}
else
{
cout << "第一次插入失败" << endl;
}
pair<set<int>::iterator, bool> result2 = s1.insert(21);
if (result2.second)//第二个数据是bool类型
{
cout << "第一次插入成功" << endl;//返回真 插入成功
}
else
{
cout << "第一次插入失败" << endl;
}
cout << string(n, '-') << endl;
cout << "multiset.insert() " << endl;
multiset<int> s2;
s2.insert(23);
s2.insert(23);
//insert的返回值类型是iterator类型 没有bool类型的返回值判断是否重复插入
for (multiset<int>::iterator it = s2.begin(); it != s2.end(); it++)
{
cout << *it << " ";
}
cout << endl;
cout << string(n, '-') << endl;
}
解释:
可以看到在第二次插入值21时,insert第二个返回值是false,插入失败,无法插入重复值。也就是说insert第二个bool类型的返回值是用来检测重复值的,如果插入的是重复值,直接返回false;如果不是重复值,返回true。这很好地体现了set容器中为什么不能插入重复值。在set容器的定义中,如果有重复值插入,会直接返回一个插入失败结果
总结:
- 如果不允许插入重复数据可以利用set
- 如果需要插入重复数据利用multiset
2 pair对组创建
功能描述: 成对出现的数据,利用对组可以返回两个数据
两种创建方式:
pair<type, type> p ( value1, value2 );
pair<type, type> p = make_pair( value1, value2 );
type是指数据类型
示例:
void test1()
{
int n = 20;
cout << "第一种方式" << endl;
pair<string, int> p1("Bella", 56);//类似默认构造 本质是类模板
cout << "姓名:" << p1.first << "体重:" << p1.second << endl;//通过 . 访问第一个和第二个数据
cout << string(n, '-') << endl;
cout << "第二种方式" << endl;
pair<string, int> p2 = make_pair("Bella", 54);
cout << "姓名:" << p2.first << "体重:" << p2.second << endl;
}
总结: 两种方式都可以创建对组,记住一种即可
3 set容器排序
学习目标: set容器默认排序规则为从小到大,掌握如何改变排序规则
主要技术点: 利用仿函数,可以改变排序规则
3.1 set存放内置数据类型
在之前的学习中,set插入数据时就会对数据进行一个排序,并且默认的排序规则是从小到大。那么如何改变这个排序规则呢?
class myCompare
{
public:
bool operator()(int value1, int value2) const
{
return value1 > value2;
}
};
void test1()
{
set<int> s1;
s1.insert(21);
s1.insert(22);
s1.insert(23);
s1.insert(24);
s1.insert(25);
cout << "默认排序规则为从小到大 s1:";
for (set<int>::iterator it = s1.begin(); it != s1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
set<int, myCompare> s2;
s2.insert(21);
s2.insert(22);
s2.insert(23);
s2.insert(24);
s2.insert(25);
cout << "指定排序规则为从大到小 s2:";
for (set<int, myCompare>::iterator it2 = s2.begin(); it2 != s2.end(); it2++)
{
cout << *it2 << " ";
}
cout << endl;
3.2 set存放自定义数据类型
class Person
{
public:
Person(string name, int age, int height, int weight)
{
this->m_name = name;
this->m_age = age;
this->m_height = height;
this->m_weight = weight;
}
string m_name;
int m_age;
int m_height;
int m_weight;
};
class myCompare
{
public:
bool operator()(const Person&p1, const Person&p2) const
{
if (p1.m_age == p2.m_age)
{
return p1.m_height > p2.m_height;
}
else if (p1.m_height == p2.m_height)
{
return p1.m_weight < p2.m_weight;
}
else
{
return p1.m_age > p2.m_age;
}
}
};
void test1()
{
//创建人物
set<Person, myCompare> s;
string nameseed[] = { "张飞", "孙权", "西施", "貂蝉", "王昭君", "关羽" };
int ageseed[] = { 28, 28, 25, 24, 26, 28 };
int heightseed[] = { 178, 170, 153, 153, 153, 200 };
int weightseed[] = { 140, 135, 108, 95, 100, 165 };
for (int i = 0; i < 6; i++)
{
Person p(nameseed[i], ageseed[i], heightseed[i], weightseed[i]);
s.insert(p);
}
cout << "自定义数据类型 set" << endl;
for (set<Person, myCompare>::iterator it = s.begin(); it != s.end(); it++)
{
cout << "姓名:" << it->m_name << "\t年龄:" << it->m_age
<< "\t身高:" << it->m_height << "\t体重:" << it->m_weight << endl;
}
cout << endl;
}
注意点
- 在set还没有插入数据之前,就要指定set的排序规则,才会按照制定的排序规则插入数据;
- set的默认构造
set<T> st;
,参数类型是类模板,因此这个仿函数也封装在类中; - 仿函数,即重载小括号,operator();
- 仿函数的返回值是bool类型;
- 仿函数的形参取决于容器插入的数据类型。如果set插入的数据是int, 那么仿函数的形参也应该是int类型,即
bool operator()(int v1, int v2) { }
。如果set插入的数据是自定义数据类型Person, 那么仿函数的形参也应该是Person类型,即bool operator()(const Person& v1, const Person& v2) { }
,这里加入const修饰,防止数据类修改;使用引用防止浅拷贝。 - 如果要访问这个容器的数据,迭代器的作用域也要相应地变化,
set<int, myCompare>::iterator
,set<Person, myCompare>::iterator
- 在仿函数最后还要加上const修饰,否则会提示如下错误
总结:
对于内置数据类型,利用仿函数可以指定set容器的排序规则即可;
对于自定义数据类型,set必须指定排序规则才可以插入数据。