• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
LOFLY
终其一生,编织快乐
博客园    首页    新随笔    联系   管理    订阅  订阅

C++之STL

STL 概论

1 STL概论

STL(标准模板库):
STL的分类:容器,算法和迭代器。
STL提供了6大组件:容器,算法和迭代器,仿函数、适配器(配接器)、空间配置器。

2 三大组件的初识

容器:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>

// 容器 vector
// 迭代器 遍历的功能
// 普通的指针 也是一种迭代器
void test01() {
	int array[5] = {1,3,5,6,8};
	int *p = array; // 指向array[0]
	for (int i = 0; i < 5;i++) {
		//cout << array[i] << " ";
		cout << *(p++) << " ";
	}
}

void test02() {
   // 声明一个容器
	vector<int> v; // 声明一个容器  这个容器中存放int类型数据  对象名称
	// 向容器中添加数据
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	// 遍历容器中的数据
	// 利用迭代器

	// 声明迭代器
	vector<int>::iterator itBegin = v.begin(); // itBegin指向的是v容器中的起始位置
	vector<int>::iterator itEnd = v.end(); // itEnd指向的是v容器中最后一个位置的下一个地址

	while (itBegin != itEnd) {
		cout << *itBegin << " ";

		itBegin++;
	}
}

int main()
{
	test02();
	system("pause");
	return EXIT_SUCCESS;
}
总结: 普通的指针也是一种迭代器 C++容器类 vector ,vector 自带的迭代器vector <T> ::iterator

四种遍历方法:
第1种:直接使用while循环

// 声明迭代器
	vector<int>::iterator itBegin = v.begin(); // itBegin指向的是v容器中的起始位置
	vector<int>::iterator itEnd = v.end(); // itEnd指向的是v容器中最后一个位置的下一个地址

	// 第1种遍历方式
	/*while (itBegin != itEnd) {
		cout << *itBegin << " ";

		itBegin++;
	}*/

第2种:使用for循环

for (vector<int>::iterator start = v.begin(); start != v.end(); start++) {
		cout << *start << " ";
	}

第3种:使用foreach循环

for ( int a: v) {
		cout << a << " ";
	}

第4种: 使用algorithm头文件中的for_each函数

for_each(v.begin(),v.end(),myPrint);
总结: 迭代器可以看作指针。
vector对象的begin()函数返回的值就是指向vector容器第一个元素的指针。

3 string容器

3.1 构造和赋值

// 1. string 构造函数
	string str;
	string str2(str); // 拷贝构造
	string str3 = str2;

	string str4("abc"); // 使用n个字符串初始化
	string str5(10, 'a'); // 用10个字符a来初始化

	cout << str4 << endl;
	cout << str5 << endl;

	// 2. string 基本的赋值
	str = "Hello";
	cout << str << endl;
	str2 = str4; // 拷贝赋值
	str = 'a';
	cout << str << endl;
	 
	str3.assign("fyeuryue",5); //将字符串的前5个数放到str3中
	cout << str3 << endl;
	str3.assign("fyeuryue", 1,5); // 将字符串的第1个字符到第5个字符拿出来赋值给str3
	cout << str3 << endl; 

3.2 存储

从string容器中取出元素的两种方法:
1. 使用方括号 [idnex]
2. 使用at(index)函数

3.3 字符串拼接

直接使用”+”运算符,或者调用append()函数

string s1 = "hello";
string s2;
s2 += " world";  // 字符串拼接

s1.append("lofly"); // 字符串拼接
cout << s1 + s2 << endl;

3.4 字符串查找

调用find()
	int position = s1.find('o', 0); 
	cout << position << endl; // 输出5  找不到返回-1
	position = s1.rfind('o');
	cout << position << endl; // 输出5  找不到返回-1

3.5 字符串替换

// 字符替换
s1.replace(0, 3, "llh"); // 从0开始到3位置结束的字符 替换为llh
cout << s1 << endl; //

3.6 字符串比较

// 字符串比较
	string t1 = "abc";
	string t2 = "abc";

	if (t1.compare(t2) == 0) {
		cout << "字符串相等" << endl;
	}
	else if(t1.compare(t2) == 1){
		cout << "t1大于t2" << endl;
	}
	else {
		cout << "t1小于t2" << endl;
	}

3.7 string子串

	// 子串
	string ss1 = "csadhfu";
	string ss2 = ss1.substr(2,5);
	cout << ss2 << endl;

3.8 string 的插入和删除

// 插入和删除
	string str1 = "hello";
	str1.insert(1,"SB");
	cout << str1 << endl;

	str1.erase(1,2); // 从位置1开始的两个字符会被删除
	cout << str1 << endl;

3.9 string 和 c-style类型转换

//string 和 c-style字符串转换
string s1 = "abc";
	const char *cstr = s1.c_str(); 

	cout << cstr << endl;
	printf("%s \n", cstr);

	string s2(cstr);
	cout << s2 << endl;

3.10 string赋值重新分配内存问题

string s = "abcdefghi";
	char &a = s[1];
	char &b = s[2];
	a = 'm';
	b = 'n';
	cout << s << endl;
	cout << "字符串的地址" << (int*)s.c_str() << endl;

	s = "ppppp";// 原始开辟的内存够用, 不重新分配内存
	s = "pppppppppppppppppppppppppppp"; // 原始开辟的内存不够用,重新分配内存

	cout << s << endl;
	cout << "字符串的地址" << (int*)s.c_str() << endl;

// 将所有的字母转成大写
	string s = "abRdefg";
	for (int i = 0; i < s.size();i++) {
		s[i] = toupper(s[i]);
	}

	cout << s << endl;

转小写使用函数 tolower()

4 vector容器

image
vector的容器容量并非是线性增加的。

4.1 vector的数据结构

vector采用的是线性连空间。

注:动态增加大小的原理:为了能有更合适的控件,需要重新开辟一个更大的空间,将原来的数据拷贝到这个空间中。对vector的操作,一但空间重新分配了,指向原vector的所有的迭代器都会失效。

4.2 常用API操作

// 构造函数
	vector<int> v; // 调用默认构造
	int a[] = {1,2,3,4,5};
	vector<int> v1(a,a+sizeof(a)/sizeof(int)); //  将a中的元素作为构造函数的参数
	vector<int> v2(v1.begin(),v1.end()); // 将v1中的原始作为构造函数的参数
	printVector(v2);

	vector<int> v3(10,100); // 10个100
	printVector(v3);

	// 赋值操作
	vector<int> v4(v3.begin(),v3.end()-1);
	printVector(v4);

	v4.swap(v2); // 容器中的元素进行互换
	cout << "v4和v2中的元素进行互换" << endl;
	printVector(v4);

	cout << v4.size() << endl; //统计容器中的元素个数

	if (v4.empty()) {
		cout << "空容器" << endl;
	}
	else {
		cout << "非空容器" << endl;
	}
	// resize(n)
	// resize(n,m) 重新设置容器的大小  如果过长 用0填充 如果过短就删除长度以外的元素
	v4.resize(10,-1);
	printVector(v4);

	// reserve(int len) // 预留len个长度空间 但预留的空间没有数据

4.3 巧用swap

vector<int> v;
	for (int i = 0; i < 100000;i++) {
		v.push_back(i);
	}
	cout << "容量" << v.capacity() << "大小" << v.size() << endl;// 容量 138225 大小100000
	v.resize(3);
	cout << "容量" << v.capacity() << "大小" << v.size() << endl;// 容量 138225 大小3

	// 巧用swap
	vector<int>(v).swap(v); // 使用v来初始化匿名对象 再用匿名对象来调用swap(v)函数
	cout << "容量" << v.capacity() << "大小" << v.size() << endl;// 容量3大小3

4.4 预留空间reserve函数

vector<int> v;
	int *p = NULL;
	int num = 0;

	for (int i = 0; i < 100000;i++) {
		v.push_back(i);

		if (p != &v[0]) {
			p = &v[0];
			num++;
		}
	}
	cout << num << endl; // 输出30  表示重新开辟了30次空间

预留空间

vector<int> v;
	v.reserve(100000);  // 预留100000个空间
	int *p = NULL;
	int num = 0;

	for (int i = 0; i < 100000;i++) {
		v.push_back(i);

		if (p != &v[0]) {
			p = &v[0];
			num++;
		}
	}
	cout << num << endl; // 输出1

4.5 vector存取元素

vector<int> v;
	v.push_back(10);
	v.push_back(11);
	v.push_back(12);
	v.push_back(13);

	cout << v.front() << endl; // 取第一个数
	cout << v.back() << endl; // 取最后一个数

	// 插入
	v.insert(v.begin(), 10000); // 第一个参数是一个迭代器

	// 删除最后一个元素
	v.pop_back();
	printVector(v);

	// 删除元素
	v.erase(v.begin()); // 参数是迭代器

	v.erase(v.begin(),v.end()); // 删除指定范围的元素
	if (v.empty()) {
		cout << "空的容器" << endl;
	}

	//  清空容器
	v.clear();

4.6 逆序遍历

	// 使用逆序的迭代器进行遍历
	for (vector<int>::reverse_iterator r = v.rbegin(); r != v.rend();r++) {
		cout << *r << " ";
	}
	cout << endl;

5 deque容器

deque头插效率更高。
Deque没有容量的概念的,是动态分段空间组成。
原理图:
image

5.1 常用API

三种迭代器

void printDeque(const deque<int> &d) {
	// 普通的迭代器 iterator 
	// 逆序迭代器 reverse_iterator 
	// const_iterator只读迭代器
	for (deque<int> ::const_iterator begin = d.begin(); begin != d.end(); begin ++) {
		
		cout << *begin << " ";
	}

构造函数跟vector类似。

deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_back(30);
	d.push_back(100);
	printDeque(d);

	deque<int> d2(d.begin(),d.end());
	d2.push_back(10000);
	printDeque(d2);

	// 交换两个deque 双向队列
	d2.swap(d);
	printDeque(d);

	if (d2.empty()) { // 判断双向队列是否为空
		cout << "为空" << endl;
	}
	else {
		cout << "不为空" << endl;
	}

	// 读取头数据 和 尾数据
	cout << d.front() << endl; // 头数据
	cout << d.back() << endl;  // 尾数据

	// 插入数据
	deque<int> d2(10,1);
	d.insert(d.begin(), 10); // 在队列头添加10 
	d.insert(d.begin(), d2.begin(), d2.end());
排序:#include <algorithm>
	deque<int> d;
	d.push_back(1);
	d.push_back(20);
	d.push_back(12);
	d.push_back(3);

	// 排序
	sort(d.begin(),d.end());  // 升序排序
	// 从大到小 排序
	sort(d.begin(), d.end(), myCampare); // 降序排序 

	printDeque(d);


随机数讲解:
导入头文件: #include <ctime>
	srand((unsigned int) time(NULL));  // srand(30);
	cout << "随机数:" << rand() << endl;

6 栈容器

导入头文件 #include <stack>
压栈:push(ele)
栈大小:size()
返回栈顶元素:top()
// 读取栈顶元素
cout << "栈顶元素为:" << s.top() << endl;
// 弹出栈顶元素
s.pop();

7 队列

导入头文件
#include <queue>
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);

cout << "最后一个元素" << q.back() << endl;
cout << "第一个元素" << q.front() << endl;

while (q.size() != 0)
{
	cout << q.front() << endl; // 输出 队头
	q.pop();
}
cout <<"队列的长度="<< q.size() << endl;

8 list容器

list容器(上)

list是基于(双向循环)链表的数据结构,vector是线性结构的.

	list<int> ls1(10,10);
	list<int> ls2(ls1.begin(),ls1.end());

	printList(ls1);

	ls2.push_back(11);
	for (list<int>::reverse_iterator rbegin = ls2.rbegin(); rbegin != ls2.rend();rbegin ++) {
		cout << *rbegin << " ";
	}
	cout << endl;

list 不支持随机访问。只能每次向后访问一个元素。

list<int> l3;
l3.push_back(10);  // 尾插法
l3.push_back(20);
l3.push_back(30);
l3.push_back(40);

l3.push_front(1000); // 头插法
//  删除两端数据
l3.pop_front(); // 删除头部数据
l3.pop_back(); // 删除尾部数据

// 移除元素
l3.remove(10); // 移除所有等于10的元素
printList(l3);

// 赋值
list<int > L4;
L4.assign(l3.begin(),l3.end());
printList(L4);

list容器(下)

list翻转排序

bool ageCompare(Person &p1,Person  &p2) {
   return p1.m_age < p2.m_age;  // 升序排序
}
class Person;
void printPersonList(list<Person> &p) {
   for (const Person p1:p) {
   	cout << p1.m_name << ":" << p1.m_age << "  ";
   }
   cout << endl;
}


   list<Person> p;
   Person p1("孙悟空",500);
   Person p2("猪八戒",30);
   Person p3("唐僧",23);
   Person p4("沙僧",300);

   p.push_back(p1);
   p.push_back(p2);
   p.push_back(p3);
   p.push_back(p4);
   
   // 按照年龄的降序输出
   p.sort(ageCompare);
   printPersonList(p);

8.1 删除操作:

// 自定义数据类型
class Person {
public :
	Person(string name,int age,int height) {
		this->m_name = name; 
		this->m_age = age;
		this->m_Height = height;
	}

	// 重载== 使得remove方法可以删除自定义的person类型
	bool operator==(const Person &p) {
		if (this->m_name == p.m_name &&  this->m_age == p.m_age && this->m_Height == p.m_Height) {
			return true;
		}
		return false;
	}

	string m_name;
	int m_age;
	int m_Height;
};

	// 删除
	p.remove(p6);
	printPersonList(p);

9 set容器(加入的元素自动排序)

multiset允许插入重复的值,set和multiset底层都是采用红黑树数据结构。

set容器是关联式容器,自动按照顺序加入容器。(自动排序)
set容器不能插入重复的值。

set<int> s;
	// 关联式容器 自动排序
	s.insert(1); // 加入元素 1
	s.insert(13);
	s.insert(7);
	s.insert(5);
	s.insert(9);
	s.insert(11);

	printSet(s);

	if (s.empty()) {
		cout << "空的容器!" << endl;
	}
	else {
		cout << "容器的大小:" << s.size() << endl;
	}

	// 两种删除元素的方法
	s.erase(s.begin());// 1.使用迭代器的方式
	s.erase(11); // 2.直接删除指定的元素 
	printSet(s)
查找操作:
set<int> s1;
	s1.insert(3);
	s1.insert(5);
	s1.insert(8);
	s1.insert(6);
set<int>::iterator pos =  s1.find(5);// 如果value存在 返回迭代器,如果不存在返回s.end()
	if (pos != s1.end()) {
		cout << "找到,值为" << *pos << endl;
	}
	else {
		cout << "没找到" << endl;
	}

// 对于set而言 count函数的返回值要么是1 要么是0
	cout <<"key值个数:" <<s1.count(8) << endl; // 查找key的个数  输出1 

	cout << s1.count(1) << endl;

	set<int>::iterator it1 = s1.lower_bound(1); // 返回第一个大于等于1的迭代器
	set<int>::iterator it2 = s1.upper_bound(6); // 返回第一个大于6的迭代器
	cout << *it1 << "\t" << *it2 << endl;

	
// 返回上下限的两个迭代器(分别对应上面的lower_bound和upper_bound)
	pair<set<int>::iterator,set<int>::iterator> ret= s1.equal_range(3);

10 pair对组的创建方式

//// 第1种 
//pair<string, int> p(string("Tom"),13);
//cout <<"姓名:" << p.first << endl;
//cout << "年龄:" << p.second << endl;
// 第2种
pair<string, int> p = make_pair(string("张三"),34);
cout <<"姓名:" << p.first << endl;
cout << "年龄:" << p.second << endl;

10.1 set容器是否可以插入成功判断

set<int> s;
pair<set<int>::iterator, bool> p;
p= s.insert(4);
cout << "插入是否成功=" << p.second << endl; //  插入成功
p = s.insert(4);
cout << "插入是否成功=" << p.second << endl; // 插入失败

10.2 set容器排序(指定set的排序规则)

// 指定set容器的排序规则 从大到小
// 仿函数
class myCompare {
public:
	// 重载
	bool operator()(int v1,int v2) {
		return v1 > v2;
	}
};

	// 从大到小排序(set一但设定好就不能对元素进行移动)
	// 只能在插入之前指定
	for (set<int, myCompare>::iterator start = s1.begin(); start != s1.end();start ++) {
		cout << *start << " ";
	} // 输出 8 6 5 3
	cout << endl;

11 set容器插入自定义数据类型

插入自定义数据时,需要指定排序规则。

class  myComparePerson {
public:
	bool operator()(const Person &p1,const Person &p2) {
		if (p1.m_age > p2.m_age) { // 降序排序
			return true;
		}
		else {
			return false;
		}
	}
};
void test06() {
	set<Person,myComparePerson> s1;
	Person p1("张三",12);
	Person p2("李四", 22);
	Person p3("王五", 42);
	Person p4("赵六", 32);

	s1.insert(p1);
	s1.insert(p2);
	s1.insert(p3);
	s1.insert(p4);

	// 显示
	for (set<Person, myComparePerson>::iterator begin = s1.begin(); begin != s1.end();begin ++) {
		cout << " 姓名:" << begin->m_name << " 年龄:" << begin->m_age <<" ";
	}
	cout << endl;
}

7 map/multimap容器

所有元素都会根据元素的键值进行排序。Map所有元素都是pair,第一个元素视为键值,第二个元素视为实值。不允许有两个相同的key值。

	map<int, int> m;
	// 4种 插入值
	// 第1种 插入值的方式
	m.insert(pair<int,int>(1,10));
	// 第2种插入值的方式(推荐)
	m.insert(make_pair(2,10));

	// 第3种插入值的方式(不推荐)
	m.insert(map<int, int>::value_type(3, 10));
	// 第4种插入值的方式(不推荐)
	m[4] = 100;

	for (map<int, int>::iterator begin = m.begin(); begin != m.end(); begin ++) {
		cout << "key=" << begin->first << " value=" << begin->second << endl;
	}

	//判断是否非空
	if (m.empty()) {
		cout << "为空" << endl;
	}
	else {
		cout << "非空" << endl;
	}

	// 查找
	map<int, int>::iterator it =  m.find(5);
	if (it != m.end()) {
		cout << "找到"<<it->second << endl;
	}
	else {
		cout << "没有找到" << endl;
	}


	// lower_bound 返回第一个 key>=keyEle元素的迭代器
	map<int, int> ::iterator ret = m.lower_bound(3);
	if (ret != m.end()) {
		cout << ret->first << " " << ret->second << endl; // 3 10
	}

	ret = m.upper_bound(3);
	if (ret != m.end()) {
		cout << ret->first << " " << ret->second << endl; // 4 100
	}




	pair<map<int,int>::iterator, map<int, int>::iterator> ret2 =   m.equal_range(3);
	if (ret2.first != m.end()) {
		cout << "查找到equal_range中的lower_bound" << ret2.first->first << " "
		<<	ret2.first->second
			<<endl;
	}
	if (ret2.second != m.end()) {
		cout << "查找到equal_range中的upper_bound" << ret2.second->first << " "
			<< ret2.second->second << endl;
	}

指定排序规则
class myCompare { // 按照key的值从大到小排序
public:
	bool operator()(int v1, int v2) {
		return v1 > v2;
	}
};

// 指定排序规则
void test02() {
	map<int, int, myCompare> m;

	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(4, 40));
	m.insert(pair<int, int>(2, 80));

	m.insert(make_pair(3, 11));
	m.insert(map<int, int>::value_type(5, 15));

	for (map<int, int, myCompare>::iterator begin = m.begin(); begin != m.end(); begin++) {
		cout << "key=" << begin->first << " value=" << begin->second << endl;
	}
}
posted @ 2022-08-30 18:23  编织快乐  阅读(64)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3