你的c++学习路上明灯
哈哈哈,今天终于可以更新关于c++知识的最后一个部分的知识了,后面的话我应该就会更新一些关于python和机器学习还有数据结构方面的东西了,毕竟写这个东西也是很花时间的,希望大家多多支持,大家的认可,真的能让我大受鼓舞的,也是一直督促我前进的动力。
一,STL基本概念
1,STL(standard template library)标准模板库
2,STL从广义上分为:容器(container),算法(algorithm),迭代器(iterator)
3,STL几乎所有的代码都采用了模板类或者模板函数
4,容器和算法之间凭借迭代器进行连接
二,STL六大组件
接下来我来分别讲一讲容器,算法,迭代器和仿函数
1,容器:
STL容器就是将运用最广泛的一些数据结构凸显出来。
关联式容器概念介绍_遥远的歌s的博客-CSDN博客_关联式容器
STL之序列式容器(一)、什么是序列式容器_学无止尽,谨言慎行!-CSDN博客
序列式容器这一篇写的是真的不错(那两张图)总结的很好。
2,算法:
有限的步骤,解决逻辑或数学上的问题
说白了,算法就是解决问题的方法
3,迭代器:
容器和算法之间的粘合剂
实质:提供一种方法,使之能够访问某个容器所含的某些元素,而又无需暴露该容器的内部的表达方式。
每个容器都有自己的专属迭代器。
4,仿函数
在STL中仿函数就是一种方法,改变一些规则的方法,其实我们学的也不多,简单了解即可。
三,详解STL中的容器
在这里,我会着重讲几个容器,其他的容器就会泛泛而谈,因为他们是相似的。
一,string容器
1,本质:string是c++风格的字符串,而string本质上是一个类。
2,
char*是一个指针。
string是一个类,类内部封装了char*,管理这个字符串,是一个char*的容器
3,特点:
string类内部封装了很多成员方法。(所有STL容器都是),string管理char*所分配的内存,不用担心复制越界和取值越界等问题,由类内部进行负责。
4,构造函数
#include<iostream> #include<string> using namespace std; void test1() { string s1="hello world"; const char* str = "hello world"; string s2(str); cout << s2 << endl; string s3(s2); cout << s3 << endl; string s4(8, 'a'); cout << s4 << endl; } int main() { test1(); return 0; }
5,赋值操作
#include<iostream> #include<string> using namespace std; void test1() { string str1; str1 = "hello world"; string str2; str2 = str1; cout << str2 << endl; //string str3='s'; //str3 = 's'; string str4; str4.assign(str2); //删除前五个 string str5; str5.assign(str1, 5); string str7; str7.assign("hello c++", 5); string str6; str6.assign(10, 'w'); cout << str5 << endl; cout << str7 << endl; } int main() { test1(); return 0; }
6,字符串拼接
#include<iostream> using namespace std; #include<string> int main() { string str1 = "我"; str1 += "爱玩游戏"; str1 += ":"; cout << "str1 = " << str1 << endl; string str2 = "LOL CF"; str1 += str2; cout << "str1 = " << str1 << endl; string str3 = "I"; str3.append(" love "); str3.append(str2, 3); cout << "str3 = " << str3 << endl; str3.append(str2); cout << "str3 = " << str3 << endl; string str4 = "I love game"; str4.append(str2, 0, 3); cout << "str4 = " << str4 << endl; return 0; }
7,字符串查找和替换
#include<iostream> #include<string> using namespace std; //字符串查找与替换 //1,查找 void test1() { string str1 = "abcdefghde"; int pos = str1.find("df"); cout << "pos = " << pos << endl; if (pos==-1) { cout << "未找到" << endl; } else { cout << "已找到,pos = " << pos << endl; } pos = str1.rfind("de"); cout << "pos = " << pos << endl; } //2.替换 void test2() { string str1 = "abcdefghde"; //虽然只是要替换三个字符,但是后面接的字符串有四个,全部替换上去 str1.replace(1, 3, "1111"); cout << "str1 = " << str1 << endl; } int main() { test1(); test2(); return 0; }
8,字符串比较
9,字符存取
#include<iostream> #include<string> using namespace std; void test1() { string str1 = "hello"; //1.利用【】访问 for (int i = 0; i < str1.size(); i++) { cout << str1[i] << " "; } cout << endl; //2,利用at访问 for (int i = 0; i < str1.size(); i++) { cout << str1.at(i) << " "; } cout << endl; } int main() { test1(); return 0; }
10,string的插入和删除
#include<iostream> #include<string> using namespace std; void test1() { //1,插入字符串 string str1 = "hello"; str1.insert(1, "111"); cout << "str1 = " << str1 << endl; //2,删除 str1.erase(1, 3); cout << "str1 = " << str1 << endl; } int main() { test1(); return 0; }
11,字串获取
#include<iostream> #include<string> using namespace std; void test1() { string str1 = "hello"; string subStr = str1.substr(1, 3); cout << "subStr = " << subStr << endl; //实用操作 string email = "zhangsan@qq.com"; int pos = email.find("@"); cout << "用户名: " << email.substr(0, pos) << endl; } int main() { test1(); return 0; }
string容器是典型的序列式容器,其他的序列式容器的操作大多相同。例如下面要讲的vector容器。
二,vector容器
1,功能:
和数组十分相似,也称为单端数组
2,与普通数组的区别:
数组是静态的,而vector可以动态扩展。
3,什么叫动态扩展?
并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝到新空间,释放原空间。(无法确定后续空间是否已经被使用)
除有与上面所有相同的操作外,还有其他的一些操作
4,vector容器存放数据
1)存放内置数据类型
#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; #include<vector> #include<algorithm>//标准算法头文件 void MyPrint(int val) { cout << val << endl; } void test() { //创建一个vector数组 vector<int> v; //写入数据 v.push_back(10); v.push_back(20); v.push_back(30); v.push_back(40); //通过迭代器访问容器中的数据 vector<int>::iterator itBegin = v.begin(); vector<int>::iterator itEnd = v.end(); //第一种遍历方式 while (itBegin != itEnd) { cout << *itBegin << endl; itBegin++; } //第二种遍历方式 for (vector<int>::iterator it = v.begin(); it != v.end(); it++) { cout << *it << endl; } //第三种遍历方式 //利用STL中的遍历算法 for_each(v.begin(), v.end(), MyPrint); } int main() { test(); return 0; }
2)存放自定义数据类型
#include<iostream> #include<vector> #include<string> using namespace std; class person { public: string m_Name; int m_Age; person(string name, int age) { this->m_Age = age; this->m_Name = name; } }; //存放对象 void test1() { vector<person> v; person p1("a", 1); person p2("a", 2); person p3("a", 3); person p4("a", 4); person p5("a", 5); v.push_back(p1); v.push_back(p2); v.push_back(p3); v.push_back(p4); v.push_back(p5); for (vector<person>::iterator it = v.begin(); it < v.end(); it++) { cout << "年龄:" << (*it).m_Age << " 姓名:" << (*it).m_Name << endl; cout << "年龄:" << it->m_Age << " 姓名:" << it->m_Name << endl; } } //存放指针 void test2() { vector<person*> v2; person p1("a", 1); person p2("a", 2); person p3("a", 3); person p4("a", 4); person p5("a", 5); v2.push_back(&p1); v2.push_back(&p2); v2.push_back(&p3); v2.push_back(&p4); v2.push_back(&p5); for (vector<person*>::iterator it = v2.begin(); it < v2.end(); it++) { cout << "年龄:" << (*it)->m_Age << " 姓名:" << (*it)->m_Name << endl; } } int main() { test1(); test2(); return 0; }
5,vector容器的迭代器是支持随机访问的迭代器。
6,vector互换容器
#include<iostream> #include<vector> using namespace std; void PrintVector(vector<int>& v) { for (vector<int>::iterator it = v.begin(); it < v.end(); it++) { cout << *it << " "; } cout << endl; } void test1() { cout << "互换前:" << endl; vector<int> v1; for (int i = 0; i < 10; i++) { v1.push_back(i); } PrintVector(v1); vector<int> v2; for (int i = 9; i >= 0; i--) { v2.push_back(i); } PrintVector(v2); //互换容器 cout << "互换后:" << endl; v1.swap(v2); PrintVector(v1); PrintVector(v2); } //实用操作,收缩内存 void test2() { vector<int> v; for (int i = 0; i < 10000; i++) { v.push_back(i); } cout << "v的容量为:" << v.capacity() << endl; cout << "v的大小为:" << v.size() << endl; //只是重新指定大小,容量不变 v.resize(3); cout << "v的容量为:" << v.capacity() << endl; cout << "v的大小为:" << v.size() << endl; //收缩内存 //当前行结束后,匿名对象的空间就会被释放 vector<int>(v).swap(v);//匿名对象 cout << "v的容量为:" << v.capacity() << endl; cout << "v的大小为:" << v.size() << endl; } int main() { test1(); test2(); return 0; }
7,预留空间
#include<iostream> #include<vector> using namespace std; void test1() { //reserve预留空间,预留位置不初始化,元素不可访问 vector<int> v1; int num = 0; int* p = NULL; v1.reserve(10000); for (int i = 0; i < 10000; i++) { v1.push_back(i); if (p != &v1[0]) { p = &v1[0]; num++; } } cout << "num = " << num << endl; } int main() { test1(); return 0; }
8,一个小知识点
vector的容量大于等于vector的大小。
9,resize用法
c++ vector resize & reserve_jackywgw的博客-CSDN博客
https://blog.csdn.net/jackywgw/article/details/6248342
三,deque容器
1,功能:
双端数组,可以对头端进行插入删除操作
2,deque与vector的区别:
1)vector对于头部的插入效率低,数据量越大,效率越低。
2)deque容器对头部的插入速度更快
3)vector容器访问元素时会更快,这和两者内部实现有关
3,deque内部工作原理:
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区内存放数据
中控器维护的是每个缓冲区的地址,使得使用deque容器时像一片连续的空间。
4,deque容器的迭代器也是支持随机访问的
5,对于支持随机访问迭代器的容器,都可以利用sort算法排序。
6,deque排序
#include<iostream> #include<deque> using namespace std; #include<algorithm> void PrintDeque(const deque<int>d) { for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++) { cout << *it << " "; } cout << endl; } void test1() { deque<int>d; d.push_back(10); d.push_back(50); d.push_back(9000); d.push_back(570); d.push_back(1); //排序算法,默认为升序 //对于支持随机访问迭代器的容器,都可以利用sort算法直接对其排序 sort(d.begin(), d.end()); PrintDeque(d); } int main() { test1(); return 0; }
7,deque构造函数
#include<iostream> using namespace std; #include<deque> void PrintDeque(const deque<int>d) { for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++) { cout << *it << " "; } cout << endl; } void test1() { deque<int>d1; for (int i = 0; i < 10; i++) { d1.push_back(i); } PrintDeque(d1); deque<int>d2(d1.begin(), d1.end()); PrintDeque(d2); deque<int>d3(10, 1000); PrintDeque(d3); deque<int>d4(d3); PrintDeque(d4); } int main() { test1(); return 0; }
四,案例
#include<iostream> #include<deque> #include<vector> using namespace std; #include<string> #include<algorithm> #include<ctime> //评委打分 class person { public: int m_Score;//分数 string m_Name;//姓名 person(string name, int score) { this->m_Name = name; this->m_Score = score; } }; void CreatePerson(vector<person>&v) { string nameseed = "ABCDE"; for (int i = 0; i < 5; i++) { string name = "选手"; name += nameseed[i]; int score = 0; person p(name, score); v.push_back(p); } } void SetScore(vector<person>&v) { for (vector<person>::iterator it = v.begin(); it != v.end(); it++) { //存放评委打的分数 deque<int>d; for (int i = 0; i < 10; i++) { int score = rand() % 41 + 60;//60~100分 d.push_back(score); } //排序 sort(d.begin(), d.end()); //去除最高和最低分 d.pop_back(); d.pop_front(); int sum = 0; for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++) { sum += *dit; } int ave = sum / d.size(); it->m_Score = ave; } } void showScore(vector<person>&v) { for (vector<person>::iterator it = v.begin(); it != v.end(); it++) { cout << "姓名:" << it->m_Name << " 平均分:" << it->m_Score << endl; } } int main() { //创造随机数种子 srand((unsigned int)time(NULL)); //1,创造5名选手 vector<person>v;//存放选手的容器 CreatePerson(v); //2,给选手打分 SetScore(v); //3.显示最后得分 showScore(v); return 0; }
五,stack容器(栈容器)
概念:一种先进后出的数据结构,它只有一个出口,栈中只有顶端的元素才能够被外界使用。
(因此栈不允许有遍历行为)
六,queue容器(队列容器)
概念:一种先进先出的数据结构,有两个出口;
队尾进数据,队头出数据。
队列中只有队头和队尾能被外界使用,因此队列不能被遍历
七,list容器(双向循环链表)
1,由于链表的存储方式并不是连续的内存空间,因此链表list只支持前移和后移,属于双向迭代器。
2,list的优点:
1)采用动态内存分配,不会造成内存的浪费和溢出
2)链表执行插入和删除操作十分方便,修改指针即可
3,缺点:
链表灵活,但是空间(指针域)和时间(遍历)额外耗费较大
4.list有一个重要的性质,插入和删除操作都不会造成原有list迭代器的失效,这在vector中是不成立的。
5,反转和排序
6.list容器-排序案例
#include<iostream> using namespace std; #include<list> //排序案例 //将person自定义数据类型进行排序, //排序规则:按照年龄进行升序,如果年龄相同按照升高进行降序 class person { public: person(string name, int age, int height) { this->m_Age = age; this->m_Height = height; this->m_Name = name; } string m_Name; int m_Age; int m_Height; }; int ComparePerson(person& p1, person& p2) { if (p1.m_Age == p2.m_Age) { return p1.m_Height > p2.m_Height; } return p1.m_Age < p2.m_Age; } void test1() { list<person>L; person p1("唐僧", 28, 180); person p2("孙悟空", 567, 159); person p3("猪八戒", 567, 179); person p4("沙僧", 567, 167); L.push_back(p1); L.push_back(p2); L.push_back(p3); L.push_back(p4); for (list<person>::const_iterator it = L.begin(); it != L.end();it++) { cout << " 姓名: " << it->m_Name << " " << " 年龄: " << it->m_Age << " " << " 身高: " << it->m_Height << endl; } cout << "------------------------------" << endl; L.sort(ComparePerson); for (list<person>::const_iterator it = L.begin(); it != L.end(); it++) { cout << "姓名: " << it->m_Name << " " << "年龄: " << it->m_Age << " " << "身高: " << it->m_Height << endl; } } int main() { test1(); return 0; }
***不支持随机访问迭代器的容器,内部都会提供一些对应的算法
八,set/multiset容器(关联式容器)
所有元素都会在插入时自动排序,
1,本质:
底层结构是二叉树
2,二者的区别:
1)set中不允许有重复元素
2)multiset中允许有重复元素
3)set插入数据的同时会返回插入结果,表示插入是否成功。
4)multiset不会检测数据,因此可以重复插入
#include<iostream> using namespace std; #include<set> void test1() { set<int>s1; pair<set<int>::iterator, bool> it = s1.insert(10); if (it.second) { cout << "第一次插入成功" << endl; } else { cout << "第一次插入失败" << endl; } it = s1.insert(10); if (it.second) { cout << "第二次插入成功" << endl; } else { cout << "第二次插入失败" << endl; } } int main() { test1(); return 0; }
3,构造与赋值
插入数据只能用insert函数
#include<iostream> #include<set> using namespace std; void PrintSet(set<int>&s) { for (set<int>::iterator it = s.begin(); it != s.end(); it++) { cout << *it << " "; } cout << endl; } void test1() { set<int>s1; //只能用insert方式 s1.insert(10); s1.insert(10); s1.insert(50); s1.insert(40); s1.insert(49); //set容器的特点,所有元素插入时候自动被排序 //set容器不允许插入重复值 PrintSet(s1); } int main() { test1(); return 0; }
4,查找与统计
#include<iostream> #include<set> using namespace std; //查找 void test1() { set<int>s; s.insert(10); s.insert(30); s.insert(40); s.insert(60); s.insert(50); set<int>::iterator pos = s.find(30); if (pos != s.end()) { cout << "找到元素。" << endl; } else { cout << "未找到元素。" << endl; } } //统计元素个数 void test2() { set<int>s; s.insert(10); s.insert(30); s.insert(40); s.insert(60); s.insert(50); //查找元素的个数 int num = s.count(30); cout << "num = " << num << endl; } int main() { test1(); test2(); return 0; }
5,排序规则
利用仿函数可以改变set容器中的排序规则
1)内置数据类型
2)自定义数据类型
#include<iostream> #include<set> using namespace std; class MyCompare { public: bool operator()(int a,int b) { return a > b; } }; void test1() { set<int>s1; s1.insert(10); s1.insert(40); s1.insert(20); s1.insert(30); for (set<int>::iterator it = s1.begin(); it != s1.end(); it++) { cout << *it << " "; } cout << endl; set<int, MyCompare>s2; s2.insert(10); s2.insert(40); s2.insert(20); s2.insert(30); for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++) { cout << *it << " "; } cout << endl; } int main() { test1(); return 0; }
九,pair对组的创建
#include<iostream> #include<string> using namespace std; void test1() { //第一种方式 pair<string, int>p1("Tom", 20); cout << "姓名: " << p1.first << "年龄:" << p1.second << endl; //第二种方式 pair<string, int>p2("Jerry", 18); cout << "姓名: " << p2.first << "年龄:" << p2.second << endl; } int main() { test1(); }
十,map/multimap容器(效率高)
map<T,T>p;
1,简介:
1)map中所有元素都是pair
2)pair中的第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
3)所有元素都会根据元素的键值自动排序
2,本质:
该容器属于关联式容器,底层结构是二叉树实现
3,优点:
可以根据key值快速找到vlaue(高效的原因)
4,map不允许重复值
multimap允许重复值
十一,注意
1)所有容器的迭代器都可进行的操作
*iter 、 ++iter 、 --iter、 iter1==iter2、 iter1!=iter2
2)vector和deque容器的迭代器可进行的额外操作
iter+n、 iter-n 、 > 、 >= 、 < 、 <=
注:这是因为vector和deque容器实际上是两个数组,只有对数组才能执行额外操作;
十二,容器嵌套容器
#include<iostream> #include<vector> #include<string> using namespace std; class person { public: string m_Name; int m_Age; person(string name, int age) { this->m_Age = age; this->m_Name = name; } }; //存放对象 void test1() { vector<person> v; person p1("a", 1); person p2("a", 2); person p3("a", 3); person p4("a", 4); person p5("a", 5); v.push_back(p1); v.push_back(p2); v.push_back(p3); v.push_back(p4); v.push_back(p5); for (vector<person>::iterator it = v.begin(); it < v.end(); it++) { cout << "年龄:" << (*it).m_Age << " 姓名:" << (*it).m_Name << endl; cout << "年龄:" << it->m_Age << " 姓名:" << it->m_Name << endl; } } //存放指针 void test2() { vector<person*> v2; person p1("a", 1); person p2("a", 2); person p3("a", 3); person p4("a", 4); person p5("a", 5); v2.push_back(&p1); v2.push_back(&p2); v2.push_back(&p3); v2.push_back(&p4); v2.push_back(&p5); for (vector<person*>::iterator it = v2.begin(); it < v2.end(); it++) { cout << "年龄:" << (*it)->m_Age << " 姓名:" << (*it)->m_Name << endl; } } int main() { test1(); test2(); return 0; }
十三,总结
1,对于自定义数据类型,必须要指定排序规则,否则编译器不知道如何排序。
2,高级排序只是在排序规则上再进行一次逻辑规则制定即可,不是很复杂。