本来还以为这一篇就能搞定基础,结果想不到的是,最后几节的知识点这么多,比较还没接触过,所以还是分为两篇,这一篇只要说map容器和函数对象,特别是对象适配器讲的比较多,对象适配器是新知识点,所以讲的细了一点。
map相对于set区别,map具有键值(key)和实值(value),所有元素根据键值自动排序,pair(对组)的第一个元素被称为key,第二个元素称为value。map也是以红黑树为底层实现的。
存储的数据如图所示,一个key和一个value。
map和multimap的区别还是map不能有key值相同,而multimap允许key值相同。
注意,不能直接修改 multimap 容器中的关键字。因为 multimap 中的元素是按照关键字排序的,当关键字被修改后,容器并不会自动重新调整顺序,于是容器的有序性就会被破坏,再在其上进行查找等操作就会得到错误的结果。
map模板类定义
// CLASS TEMPLATE map
template <class _Kty, class _Ty, class _Pr = less<_Kty>, class _Alloc = allocator<pair<const _Kty, _Ty>>>
class map : public _Tree<_Tmap_traits<_Kty, _Ty, _Pr, _Alloc, false>> {
using value_type = pair<const _Kty, _Ty>;
}
我们目前比较关系的是前面两个参数,class _Kty 就是key,class _Ty是value,第三个参数我在set容器中,讲过了,就是制定排序顺序的,它有一个默认参数,可以不填。
构造函数都很熟悉了吧,只不过map
int main(int argc, char **argv)
{
map<int, string> mymap; //调用无参构造函数
//第一种,申请pair匿名对象插入
//pair
auto ret = mymap.insert(pair<int ,string>(1, "李西"));
//这个插入的返回值比较麻烦,上面屏蔽掉的,就是类型,c++ 11中可以yogaauto匹配类型
if(ret.second) {
cout << "插入成功" << endl;
} else {
cout << "插入失败" << endl;
}
ret = mymap.insert(pair<int ,string>(1, "李西"));
if(ret.second) {
cout << "插入成功" << endl;
} else {
cout << "插入失败" << endl;
}
//第二种,通过make_pair方法创建pair对象插入
mymap.insert(make_pair(2, "赵五"));
//第三种,通过value_type的方式插入
mymap.insert(map<int, string>::value_type(3, "老王"));
//第四种,通过数组方法插入
mymap[1] = "小李";
mymap[5] = "老李";
//发现如果key不存在,创建pair插入到map容器中,
//如果发现key存在,那么会修改key对应的value值
//如果通过[]方式去访问map中一个不存在key,
//那么map会将这个访问的key插入到map中,并且给value一个默认值
cout << "mymap[7] = " << mymap[7] << endl;
for(map<int, string>::iterator it = mymap.begin(); it != mymap.end(); it++) {
cout << "第一个 " << it->first << " 第二个 " << it->second << endl;
}
return 0;
}
例子中,有插入的四种方法,和说明。
查找操作比较复杂一点,我们练练手:
count需要注意,如果是map的话,返回的不是0就是1,如果是multimap的话,返回一个key的个数,需要遍历用的。
int len = mymap.count(1); //count用int类型接
cout << "len = " << len << endl;
pair<map<int, string>::iterator, map<int, string>::iterator> ret1 = mymap.equal_range(2);
//这个返回值更长,先返回一个对组,然后对组中包含两个map的迭代器
cout << "第一个 " << ret1.first->first << " 第二个 " << ret1.first->second << endl;
cout << "第一个 " << ret1.second->first << " 第二个 " << ret1.second->second << endl;
我们容器的元素是浅拷贝进去的,也就是如果遇到指针要申请内存的话,需要深拷贝,也就是要重载拷贝构造函数和等号操作符。
#include
#include
#include
#include
class Person
{
public:
int age;
int id;
char *name;
Person(char*name, int age, int id): age(age), id(id){
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
cout << "构造函数" << endl;
}
bool operator()(Person& p){
cout << "名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id << endl;
}
~Person() {
cout << "析构函数" << &(this->name) << endl;
if(this->name != NULL){
delete[] this->name;
//this->name = NULL;
}
}
//set容器排序,需要用到<号,也可以像上面那个用仿函数
bool operator<(const Person& anthor) const {
return this->age > anthor.age;
}
};
int main(int argc, char **argv)
{
Person p1("lizi", 10, 1);
Person p2("zhaoxi", 13, 2);
Person p3("wangwu", 15, 3);
Person p4("xixi", 16, 4);
vector<Person> vp;
vp.push_back(p1);
vp.push_back(p2);
vp.push_back(p3);
vp.push_back(p4);
//for_each(vp.begin(), vp.end(), Person("jj", 12, 9));
return 0;
}
不知道g++具体在内部实现了什么,在添加4个之后,就出现了释放有问题,这个是结果:
然后我们把拷贝构造函数修改成深拷贝,(=号操作符重载也改成深拷贝):
Person(const Person& p) {
cout << "拷贝构造函数 " << endl;
this->age = p.age;
this->id = p.id;
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, p.name);
}
Person& operator=(const Person& p){
cout << "等号操作符 ===================" << endl;
//1.防止自身赋值
if (this == &p) {
return *this;
}
//2.释放原来的内存
if(this->name != NULL) {
cout << "this->name有值" << endl;
delete[] this->name;
this->name = NULL;
this->id = 0;
}
this->id = p.id;
this->age = p.age;
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, p.name);
return *this;
}
执行的结果:
虽然我也不清楚为什么拷贝构造这么多次,析构这么多次,反正总的来说不崩溃了。有谁知道的,可以留言告诉我,谢谢。
共性:
vector和deque比较:
vector.at()比deque.at()效率高,vector.at(0)是固定,deque.at(0)是不确定的
如果需要大量释放的话,vector花的时间更少。
deque支持头部快速插入和删除。
重载“()”操作符,使得类对象可以像函数那样调用,所以称为函数对象或仿函数。
注意:
重载的Operator()要求传一个参数,我们就将这个类称为“一元仿函数”,如果需要两个参数就“二元仿函数”。函数对象也可以有参数和返回值。
谓词是指普通函数或**重载的operator()**返回值是bool类型的函数对象(仿函数)。如果operator()能接受一个函数,那就叫一元谓词,那么接受两个参数,就叫二元谓词。
STL内建了一些函数对象。分为算法类函数对象,关系运算类函数对象,逻辑运算类仿函数。这些仿函数和函数对象,跟一般函数用法一样。使用内建函数对象时,需要引用头文件#include。
plus<int> pl; //这个是定义了一个对象,然后再调用仿函数
pl(10, 20);
int aa = plus<int>()(10, 20); //申请一个匿名对象 然后调用函数对象
cout << "aa = " << aa << endl;
函数对象适配器是完成一些配接工作,这些配接包括绑定(bind),否定(negate),以及对一般函数或成员函数的修饰,使其成为函数对象。
bind1st :将参数绑定为函数对象的第一个参数
bind2nd :将参数绑定为函数对象的第二个参数
not1 :对一元函数对象取反
not2 :对二元函数对象取反
ptr_fun :将普通函数修饰成函数对象
mem_fun :修饰成员函数
mem_fun_ref : 修饰成员函数
我们先看原来for_each函数操作:
int main(int argc, char **argv)
{
Person p1("lizi", 10, 1);
Person p2("zhaoxi", 13, 2);
Person p3("wangwu", 15, 3);
Person p4("xixi", 16, 4);
vector<Person> vp;
vp.push_back(p1);
vp.push_back(p2);
vp.push_back(p3);
vp.push_back(p4);
for_each(vp.begin(), vp.end(), Person());
return 0;
}
//Person仿函数
class Person
{
public:
int age;
int id;
char *name;
Person() {
this->age = 0;
this->id = 0;
this->name = new char[1];
strcpy(this->name, " ");
}
Person(char*name, int age, int id): age(age), id(id){
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
//cout << "构造函数" << endl;
}
Person(const Person& p) {
//cout << "拷贝构造函数 " << endl;
this->age = p.age;
this->id = p.id;
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, p.name);
}
Person& operator=(const Person& p){
//cout << "等号操作符 ===================" << endl;
//1.防止自身赋值
if (this == &p) {
return *this;
}
//2.释放原来的内存
if(this->name != NULL) {
cout << "this->name有值" << endl;
delete[] this->name;
this->name = NULL;
this->id = 0;
}
this->id = p.id;
this->age = p.age;
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, p.name);
return *this;
}
~Person() {
//cout << "析构函数" << &(this->name) << endl;
if(this->name != NULL){
delete[] this->name;
//this->name = NULL;
}
}
bool operator()(Person& p){
cout << "名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id << endl;
}
//set容器排序,需要用到<号,也可以像上面那个用仿函数
bool operator<(const Person& anthor) const {
return this->age > anthor.age;
}
};
执行结果:
这样遍历就很正常了,但是如果我们需要让id添加任意数的时候,我们会修改()号操作符的函数:
bool operator()(Person& p, int id_base){
cout << "名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id + id_base << endl;
}
但是我们的for_each()函数
template <class _InIt, class _Fn>
_CONSTEXPR20 _Fn for_each(_InIt _First, _InIt _Last, _Fn _Func) { // perform function for each element [_First, _Last)
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_First);
const auto _ULast = _Get_unwrapped(_Last);
for (; _UFirst != _ULast; ++_UFirst) {
_Func(*_UFirst); //只能接受一个参数
}
return _Func;
}
通过看源码发现只能接受一个参数,这时候就要适配器了。
class Person : public binary_function<int, Person, bool> //int int 是参数类型, bool是返回值
{
public:
int age;
int id;
char *name;
Person() {
this->age = 0;
this->id = 0;
this->name = new char[1];
strcpy(this->name, " ");
}
Person(char*name, int age, int id): age(age), id(id){
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
//cout << "构造函数" << endl;
}
Person(const Person& p) {
//cout << "拷贝构造函数 " << endl;
this->age = p.age;
this->id = p.id;
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, p.name);
}
Person& operator=(const Person& p){
//cout << "等号操作符 ===================" << endl;
//1.防止自身赋值
if (this == &p) {
return *this;
}
//2.释放原来的内存
if(this->name != NULL) {
cout << "this->name有值" << endl;
delete[] this->name;
this->name = NULL;
this->id = 0;
}
this->id = p.id;
this->age = p.age;
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, p.name);
return *this;
}
~Person() {
//cout << "析构函数" << &(this->name) << endl;
if(this->name != NULL){
delete[] this->name;
//this->name = NULL;
}
}
bool operator()(Person& p, int id_base) const {
cout << "名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id + id_base << endl;
}
bool operator()(int id_base, Person& p) const {
cout << "aa名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id + id_base << endl;
}
// Person& operator+(int base) {
// //cout << "名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id + id_base << endl;
// this->age += base;
// }
//set容器排序,需要用到<号,也可以像上面那个用仿函数
bool operator<(const Person& anthor) const {
return this->age > anthor.age;
}
};
#include
#include
int main(int argc, char **argv)
{
Person p1("lizi", 10, 1);
Person p2("zhaoxi", 13, 2);
Person p3("wangwu", 15, 3);
Person p4("xixi", 16, 4);
vector<Person> vp;
vp.push_back(p1);
vp.push_back(p2);
vp.push_back(p3);
vp.push_back(p4);
for_each(vp.begin(), vp.end(), bind1st(Person(), 100));
return 0;
}
感觉代码老是重复,下次不会了,下次值放差异部分,
//需要继承特定的类,才能调用适配器,后面的模板参数是,参数、参数、返回值
class Person : public binary_function<int, Person, bool>
//这就是for_each使用了适配器的方法
//适配器就是把二元函数对象转变成一元函数对象。
for_each(vp.begin(), vp.end(), bind1st(Person(), 100));
bind1st bind2nd的区别就是:
bind1st 绑定为函数的第一个参数
bind2nd 绑定为函数的第二个参数
sort(vp.begin(), vp.end(), compare()); //这是原来的排序函数
sort(vp.begin(), vp.end(), not2(compare())); //这是在原理的排序函数基础上,反转排序
class compare : public binary_function<Person, Person, bool>{
public:
bool operator()(const Person& p1, const Person& p2) const { //记得要加const
return p1.age > p2.age;
}
};
not1:对一元谓词取反
not2:对二元谓词取反
这两个不是仿函数,只是两个需要继承的类,看例子:
class compare2 : public unary_function<Person, bool>{ //函数参数为一个的时候
public:
bool operator()(const Person& p1) const {
return p1.age > 5;
}
};
vector<Person>::iterator it = find_if(vp.begin(), vp.end(), compare2());
if(it == vp.end()) {
cout << "没有找到 " << endl;
} else {
cout << *it << endl;
}
这个我们使用find_if算法,这个算法是查找第一个大于compare2里面的值,这个这个回调函数写死成一个参数,直接判断。
就是写成了一个参数之后,我们发现继承的类是unary_function,所以得出结论:
binary_function : 函数参数是两个的时候,继承这个类
unary_function : 函数参数是一个的时候,继承这个类
ptr_fun : 把普通函数变成函数对象
例子:
bool show3(int id_base, int v) {
cout << v << " " << id_base << endl;
}
vector<int> ip;
ip.push_back(1);
ip.push_back(2);
ip.push_back(3);
ip.push_back(4);
for_each(ip.begin(), ip.end(), bind1st(ptr_fun(show3), 100));
for_each其实可以直接接收普通函数,但是当普通函数需要,添加参数的时候,bind1st是不能普通函数的,只能用函数对象,所以需要转换一下,有一个疑问,就是自定义类的时候,老是有问题,不知道什么原因。
mem_fun/mem_fun_ref : 使用成员函数打印,例子:
class Person{
void show() { //person添加新的函数
cout << "show 名字 : " << this->name << " 年龄 : " << this->age << " id :" << this->id << endl;
}
}
vector<Person> vp;
vp.push_back(p1);
vp.push_back(p2);
vp.push_back(p3);
vp.push_back(p4);
for_each(vp.begin(), vp.end(), mem_fun_ref(&Person::show));
vector<Person*> vpp;
vpp.push_back(&p1);
vpp.push_back(&p2);
vpp.push_back(&p3);
vpp.push_back(&p4);
cout << "======================" << endl;
for_each(vpp.begin(), vpp.end(), mem_fun(&Person::show));
这个是存放指针的,运行结果:
也是可以打印的,由上面的例子总结:
如果存放的是对象的指针,使用mem_fun
如果存放的是对象,使用mem_fun_ref
这一篇写的不好,有点乱,知识点比较多,并且零散,导致总结不到位,代码重复很多,如果都贴出来,逻辑比较清晰,但是占用的字数太多,所以就贴了一点差异的,整体代码现在贴在下面,看着整体的代码会比较清晰一点。
class Person : public binary_function<int, Person, bool> //int int 是参数类型, bool是返回值
{
public:
int age;
int id;
char *name;
Person() {
this->age = 0;
this->id = 0;
this->name = new char[1];
strcpy(this->name, " ");
}
Person(char*name, int age, int id): age(age), id(id){
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
//cout << "构造函数" << endl;
}
Person(const Person& p) {
//cout << "拷贝构造函数 " << endl;
this->age = p.age;
this->id = p.id;
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, p.name);
}
Person& operator=(const Person& p) { //这个const引发的血案
//cout << "等号操作符 ===================" << endl;
//1.防止自身赋值
if (this == &p) {
return *this;
}
//2.释放原来的内存
if(this->name != NULL) {
//cout << "this->name有值" << endl;
delete[] this->name;
this->name = NULL;
this->id = 0;
}
this->id = p.id;
this->age = p.age;
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, p.name);
return *this;
}
~Person() {
//cout << "析构函数" << &(this->name) << endl;
if(this->name != NULL){
delete[] this->name;
//this->name = NULL;
}
}
void show() {
cout << "show 名字 : " << this->name << " 年龄 : " << this->age << " id :" << this->id << endl;
}
// bool operator()(Person& p, int id_base) const {
// cout << "名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id + id_base << endl;
// }
bool operator()(int id_base, Person& p) const {
cout << "aa名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id + id_base << endl;
}
friend ostream& operator<<(ostream& os, const Person& p);
// Person& operator+(int base) {
// //cout << "名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id + id_base << endl;
// this->age += base;
// }
//set容器排序,需要用到<号,也可以像上面那个用仿函数
bool operator<(const Person& anthor) const { //sort排序 是使用这个判断的
return this->age < anthor.age;
}
};
//用这种也可以
class compare : public binary_function<Person, Person, bool>{
public:
bool operator()(const Person& p1, const Person& p2) const { //记得要加const
return p1.age > p2.age;
}
};
class compare2 : public unary_function<Person, bool>{ //函数参数为一个的时候
public:
bool operator()(const Person& p1) const {
return p1.age > 5;
}
};
ostream& operator<<(ostream& os, const Person& p) {
os << "aa名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id << endl;
return os;
}
void show(const Person& p) {
cout << "aa名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id << endl;
}
bool show2(int id_base, Person& p) {
cout << "ab名字 : " << p.name << " 年龄 : " << p.age << " id :" << p.id + id_base << endl;
}
bool show3(int id_base, int v) {
cout << v << " " << id_base << endl;
}
int main(int argc, char **argv)
{
Person p1("lizi", 66, 1);
Person p2("zhaoxi", 13, 2);
Person p3("wangwu", 17, 3);
Person p4("xixi", 4, 4);
vector<Person> vp;
vp.push_back(p1);
vp.push_back(p2);
vp.push_back(p3);
vp.push_back(p4);
vector<int> ip;
ip.push_back(1);
ip.push_back(2);
ip.push_back(3);
ip.push_back(4);
for_each(vp.begin(), vp.end(), bind1st(Person(), 100));
//sort(vp.begin(), vp.end(), not2(compare())); //内部执行了等号操作符的重载
sort(vp.begin(), vp.end(), compare());
cout << "======================" << endl;
for_each(vp.begin(), vp.end(), bind1st(Person(), 100));
cout << "======================" << endl;
vector<Person>::iterator it = find_if(vp.begin(), vp.end(), compare2());
if(it == vp.end()) {
cout << "没有找到 " << endl;
} else {
cout << *it << endl;
}
for_each(vp.begin(), vp.end(), show); //普通函数也可以作为回调函数
cout << "======================" << endl;
//for_each(vp.begin(), vp.end(), bind1st(ptr_fun(show2), 100)); //不知道类的时候,为什么有问题
for_each(ip.begin(), ip.end(), bind1st(ptr_fun(show3), 100));
cout << "======================" << endl;
for_each(vp.begin(), vp.end(), mem_fun_ref(&Person::show));
vector<Person*> vpp;
vpp.push_back(&p1);
vpp.push_back(&p2);
vpp.push_back(&p3);
vpp.push_back(&p4);
cout << "======================" << endl;
for_each(vpp.begin(), vpp.end(), mem_fun(&Person::show));
return 0;
}