【C++提高编程-03】----C++之STL常用容器基础实战

🎩 欢迎来到技术探索的奇幻世界👨‍💻

📜 个人主页@一伦明悦-CSDN博客

✍🏻 作者简介: C++软件开发、Python机器学习爱好者

🗣️ 互动与支持💬评论      👍🏻点赞      📂收藏     👀关注+

如果文章有所帮助,欢迎留下您宝贵的评论,

点赞加收藏支持我,点击关注,一起进步!

前言

     STL(Standard Template Library)是C++标准库中的一个重要组成部分,提供了一系列通用的模板类和函数,用于实现常见的数据结构和算法。STL容器是STL中最常用的组件之一,用于存储和管理数据。以下是关于STL容器的详细介绍:

  1. 序列容器

    • vector:动态数组,支持随机访问和动态大小调整。
    • deque:双端队列,支持在两端快速插入和删除元素。
    • list:双向链表,支持高效的插入和删除操作。
  2. 关联容器

    • set:基于红黑树的有序集合。
    • map:基于红黑树的有序映射(键-值对)。
    • multiset:允许重复值的有序集合。
    • multimap:允许重复键的有序映射。
  3. 无序关联容器

    • unordered_set:基于哈希表的无序集合。
    • unordered_map:基于哈希表的无序映射。
    • unordered_multiset:允许重复值的无序集合。
    • unordered_multimap:允许重复键的无序映射。
  4. 容器适配器

    • stack:栈,基于deque或list实现。
    • queue:队列,基于deque或list实现。
    • priority_queue:优先队列,基于vector实现。
  5. 智能指针

    • shared_ptr:共享所有权的智能指针,基于引用计数实现。
    • unique_ptr:独占所有权的智能指针,禁止复制。
    • weak_ptr:弱引用智能指针,不增加引用计数。

        STL容器提供了丰富的接口和算法,使得数据的存储、操作和管理变得更加便捷和高效。在使用STL容器时,可以根据具体需求选择合适的容器类型,并结合迭代器、算法等功能来完成复杂的数据处理任务。STL容器的广泛应用也使得C++成为一种强大的编程语言,适用于各种领域的开发和编程。

正文

01-STL基础知识

        STL(Standard Template Library)是C++标准库的一部分,提供了一套通用的模板类和函数,用于实现常见的数据结构和算法。STL包括容器(Containers)、算法(Algorithms)和迭代器(Iterators)三大组件,为C++程序员提供了丰富、高效的数据结构和算法支持。

/*  长久以来,软件界一直希望建立一种可重复利用的东西
    C++的面向对象和泛型编程思想,目的就是复用性的提升
    大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
    为了建立数据结构和算法的一套标准,诞生了STL
*/

//  STL(Standard Template Library,标准模板库)
//  STL广义上分为容器(container)、算法(algorithm)、迭代器(iterator)
//  容器和算法之间通过迭代器进行无缝连接。
//  STL 几乎所有的代码都采用了模板类或者模板函数

//  STL六大组件:容器,算法,迭代器,仿函数,适配器,空间配置器

/*  1、容器分为两种  12354
    序列式容器: 强调值的排序,序列式容器中的每个元素均有固定的位置。 12354   按初始位置
    关联式容器: 二叉树结构,各元素之间没有严格的物理上的顺序关系     12345   按顺序排序
*/


/*  2、质变算法和非质变算法。    12345
    质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等      1234  改变值
    非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等    12345  并未改变

*/

/*  3、提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
      每个容器都有自己专属的迭代器,迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针

      双向迭代器 读写操作,并能向前和向后操作 读写,支持++、--,

      随机访问迭代器  读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器
      读写,支持++、--、[n]、-n、<、<=、>、>=
      常用的容器中迭代器种类为双向迭代器,和随机访问迭代器

*/

        容器(Containers)

  • 容器是STL中最重要的组成部分之一,用于存储和管理数据。常见的STL容器包括 vector、deque、list、set、map 等。下面以 vector 和 map 容器为例进行具体的代码分析:

  • vector(动态数组):用于存储动态大小的元素序列,支持随机访问和动态增删操作,是使用最频繁的容器之一。

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

int main() {
    // 创建一个存储 int 类型数据的 vector
    vector<int> vec;

    // 向 vector 中添加数据
    vec.push_back(10);
    vec.push_back(20);
    vec.push_back(30);

    // 遍历 vector 中的数据
    for (int i = 0; i < vec.size(); ++i) {
        cout << vec[i] << " ";
    }

    return 0;
}

        map(映射):用于存储键值对的集合,支持快速的查找和插入操作,适合用于建立键到值的映射关系。

#include <iostream>
#include <map>
using namespace std;

int main() {
    // 创建一个存储 string 类型键和 int 类型值的 map
    map<string, int> myMap;

    // 插入键值对
    myMap["apple"] = 10;
    myMap["banana"] = 20;
    myMap["orange"] = 30;

    // 遍历 map 中的键值对
    for (const auto &pair : myMap) {
        cout << pair.first << ": " << pair.second << endl;
    }

    return 0;
}

        算法(Algorithms)

  • STL中还提供了丰富的算法函数,用于对容器中的数据进行操作,例如排序、查找、变换等。这些算法函数可以结合容器和迭代器进行灵活的数据处理。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5};

    // 对 vector 中的数据进行排序
    sort(vec.begin(), vec.end());

    // 遍历排序后的 vector
    for (int num : vec) {
        cout << num << " ";
    }

    return 0;
}

         STL通过提供丰富、高效的容器和算法,简化了C++程序的开发过程,提高了代码的可读性和可维护性。程序员可以灵活运用STL中的容器和算法来处理数据,在实际开发中大大提高了开发效率。通过深入学习STL的使用,程序员能够更好地掌握C++编程技能,写出高质量的代码。

02-Vector数组-存放内置数据类型

        Vector是C++标准库中的一种容器,用于存储一组同类型的元素,其中也可以存放内置数据类型。下面详细解释Vector数组存放内置数据类型的相关内容:

  1. 定义和声明

    • 使用vector需要包含头文件 <vector>
    • 可以通过声明 vector<数据类型> 变量名 来定义一个vector。
  2. 初始化

    • 可以通过赋值语句、列表初始化、拷贝初始化等方式进行初始化。
  3. 插入和删除元素

    • 使用 push_back() 方法在vector末尾插入元素。
    • 使用 pop_back() 方法删除vector末尾的元素。
    • 使用 insert() 方法在指定位置插入元素。
    • 使用 erase() 方法删除指定位置或指定范围的元素。
  4. 访问元素

    • 可以使用下标访问 [] 或 at() 方法访问指定位置的元素。
    • 可以使用迭代器进行元素访问。
  5. 动态调整大小

    • Vector可以动态调整大小,即在运行时改变元素数量,使用 resize() 方法实现。
  6. 内存管理

    • Vector自动管理内存,当元素数量超过当前容量时,会自动扩展容量以容纳更多元素。
  7. 常用操作

    • size():返回vector中元素的个数。
    • empty():判断vector是否为空。
    • clear():清空vector中的所有元素。
    • swap():交换两个vector的内容。

示例代码

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

int main() {
    // 定义一个存放整数的vector
    vector<int> vec;

    // 向vector中插入元素
    vec.push_back(10);
    vec.push_back(20);
    vec.push_back(30);

    // 遍历vector并输出元素
    for (int i = 0; i < vec.size(); ++i) {
        cout << vec[i] << " ";
    }
    cout << endl;

    // 使用迭代器访问元素
    vector<int>::iterator it;
    for (it = vec.begin(); it != vec.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // 删除末尾的元素
    vec.pop_back();

    // 重新遍历vector并输出元素
    for (int num : vec) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

        下面给出具体代码分析应用过程:这段代码主要展示了使用vector存放内置数据类型(这里是整型数据)并遍历输出的过程。以下是对这部分代码的简要分析:

        头文件引入和命名空间声明:引入了 <iostream><vector> 和 <algorithm> 头文件。使用了 std 命名空间,可以直接使用 std::cout 和 std::endl 等符号,避免了在代码中频繁写 std::

        定义了自定义函数 myPrint:函数 myPrint 的作用是输出传入的整数参数,并换行。该函数用于 for_each 算法的回调函数。

   test01 函数:在函数 test01 中,首先创建了一个 vector<int> 容器 v,并向其中插入了四个整数元素(10、20、30、40)。注释掉了使用迭代器进行遍历的代码,通过两种不同的注释掉代码段可以看到迭代器遍历方式的不同,包括 while 和 for 循环遍历方式。使用 for_each 算法对 v 容器中的元素进行遍历,并通过回调函数 myPrint 输出每个元素。

   main 函数:在 main 函数中调用了 test01 函数,展示了如何使用 vector 存放数据并进行遍历输出。最后通过 system("pause") 命令使程序在执行完毕后暂停,防止窗口一闪而过。

        总体来说,这段代码通过展示了使用 vector 存放整型数据并使用不同的方法进行遍历输出,同时使用 for_each 算法简化了遍历的过程。通过这段代码可以了解如何使用 vector 容器和一些基本的STL算法,提高代码的可读性和简洁性。

#include <iostream>
using namespace std;
#include <vector>     // 包含数组容器的头文件
#include <algorithm>   // 使用STL中包含的算法时,必须包含这个头文件

// vector 存放内置数据类型   和模板那里很像

void myPrint(int val)
{
	cout << val << endl;
}

void test01()
{
	// 创建一个int类型的vector容器,数组
	vector<int> v;

	// 向容器中插入数据
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);

	// 通过迭代器访问元素   iterator 迭代器相当于一个指针,因此取出数据时使用解引用方式
// 	vector<int>::iterator itBegin = v.begin();  // 起始迭代器,指向容器中第一个元素
// 	vector<int>::iterator itEnd = v.end();      // 结束迭代器,指向容器中最后一个元素的后面一个位置,相当于指向空元素
// 
// 	// 第一种遍历方式
// 	while (itBegin!=itEnd)     // 这里需要加一个判断语句,当元素指向到于itEnd相同时,则不再进行输出
// 	{
// 		cout << *itBegin << endl;  // 容器相当于一个指针,使用解引用的方式取出数据
// 		itBegin++;            // 指针遍历数组,需要对指针进行++操作,因为数组中一个元素占有4个字节,而指针也是占有4个字节
// 
// 	}

	// 第二种方式,使用for循环  第一种太过于复杂
// 	for (vector<int>::iterator it = v.begin(); it != v.end();it++)
// 	{
// 		cout << *it << endl;
// 	}
	// 第三中使用算法  遍历算法 for_each 
	for_each(v.begin(), v.end(), myPrint);   // 算法中传入三个参数  起始迭代器,结束迭代器,函数回调
	//  这里myPrint()函数传入参数为使用指针的主要原因,是因为,算法内部已经做了解引用的操作,因此返回的就是一个整型数据,并不是地址,
	//  那么函数中传入参数,也不需要在加指针



}

int main() {
    
	test01();
	system("pause");
	return 0;
}

03-Vector存放自定义数据类型

        当向vector容器中存放自定义的数据类型时,通常需要注意以下几个方面:

  1. 定义自定义数据类型

    • 首先需要定义一个自定义的数据类型,可以是类或结构体。这个数据类型可以包含多个不同类型的成员变量,表示自定义的数据结构。
    • 在定义类或结构体时,通常需要重载比较运算符或提供自定义的排序规则,以便在vector中对自定义数据类型进行排序和查找操作。
  2. vector中存储自定义数据类型

    • 创建一个vector容器,容器的模板参数为自定义的数据类型。
    • 使用push_back()函数向vector容器中添加自定义数据类型的对象。
  3. 遍历和访问

    • 可以通过下标操作或迭代器遍历vector中存储的自定义数据类型对象。
    • 也可以使用算法函数对vector中的自定义数据类型对象进行处理,比如排序、查找等操作。

        下面是一个简单的示例代码,展示了如何在vector容器中存储自定义的数据类型(这里以学生类为例)并遍历输出:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 自定义学生类
class Student {
public:
    string name;
    int age;

    Student(string n, int a) : name(n), age(a) {}
};

// 打印学生信息的函数
void printStudent(const Student &s) {
    cout << "Name: " << s.name << ", Age: " << s.age << endl;
}

int main() {
    // 创建一个存放学生对象的vector
    vector<Student> students;

    // 向vector中插入学生对象
    students.push_back(Student("Alice", 20));
    students.push_back(Student("Bob", 22));
    students.push_back(Student("Cathy", 21));

    // 遍历vector并输出学生信息
    for_each(students.begin(), students.end(), printStudent);

    return 0;
}

        下面给出具体代码分析应用过程:这段代码演示了在vector容器中存放自定义数据类型(Person类)的两种方式:一种是存放对象本身,另一种是存放对象的指针。接下来是对这段代码的简要解释:

  1. 定义自定义数据类型 Person

    • Person 类包括 m_Name 和 m_Age 两个成员变量,分别表示人的姓名和年龄。
    • 类中定义了构造函数,用来初始化 Person 对象的姓名和年龄。
  2. test01 函数

    • 首先创建一个vector<Person>类型的容器v
    • 实例化了五个 Person 对象,并通过push_back方法将它们添加到容器中。
    • 使用迭代器遍历容器中的元素,通过 it->m_Name 和 it->m_Age 访问每个 Person 对象的姓名和年龄进行输出。
  3. test02 函数

    • 创建了一个vector<Person*>类型的容器v,存放的是 Person 对象的指针。
    • 将五个 Person 对象的地址添加到容器中。
    • 使用迭代器遍历容器中的元素,通过 (*it)->m_Name 和 (*it)->m_Age 访问每个 Person 对象的姓名和年龄进行输出,这里使用指针的箭头操作符 -> 来访问指针指向的对象的成员变量。
  4. main 函数

    • 在 main 函数中依次调用了 test01 和 test02 函数,展示了两种不同存储方式的遍历输出结果。
    • 最后通过 system("pause"); 命令使程序在执行完毕后暂停,防止窗口一闪而过。

        通过这段代码,展示了如何在vector容器中存放自定义数据类型对象或指针,并通过迭代器遍历容器中的元素进行访问和输出。同时也展示了对象指针和对象本身在vector中的存储和访问方式的差异。

#include <iostream>
using namespace std;
#include<vector>
#include<string>

// vector存放自定义数据类型   自定义person类型

class Person
{


public:

	// 使用构造函数赋初值
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	

	string m_Name;
	int m_Age;
};

void test01()
{

	vector<Person>v;

	// 创建实例化对象
	Person p1("A", 10);
	Person p2("B", 20);
	Person p3("C", 30);
	Person p4("D", 40);
	Person p5("E", 50);

	// 向容器中插入数据
	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++)// 这里直接it++
	{
		// *it解引用出来的就是Person数据类型,如果需要用的话直接查找类里的成员属性即可
//		cout << "姓名:"<<(*it).m_Name <<" 年龄:"<<(*it).m_Age<< endl;   // 这里相当于又创建了实例化对象,需要取出成员属性
		cout << "姓名:" << it->m_Name << " 年龄:" << it->m_Age << endl;  // 因为it就是指针,也可以这样使用
	}


}

// 存放自定义数据类型   指针

void test02()
{
	vector<Person*>v;

	// 创建实例化对象
	Person p1("A", 10);
	Person p2("B", 20);
	Person p3("C", 30);
	Person p4("D", 40);
	Person p5("E", 50);

	// 向容器中插入数据
	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++)
	{
		// 上面(*it)返回就是一个Person数据类型,相当于实例化对象,直接.成员属性就行
		// 而这里(*it)返回的是一个Person*数据类型,(*it)是Person类的指针,使用->取出数据
		cout << "姓名:" << (*it)->m_Name << " 年龄:" << (*it)->m_Age << endl;
	}
}


int main() {

	test01();
	test02();
	system("pause");
	return 0;
}

04-Vector容器嵌套容器

       当涉及到在vector容器中嵌套另一个容器时,通常会有一些需要注意的方面。这种情况下,你可以创建一个容器,该容器的元素是另一个容器,从而实现嵌套的效果。下面是关于在vector容器中嵌套容器的详细解释:

  1. 定义嵌套容器

    • 首先,需要定义内部容器和外部容器的数据类型。内部容器可以是任何STL容器,比如vectorlist等。
    • 外部容器的元素类型是内部容器,因此外部容器的模板参数是内部容器的类型。
  2. 操作嵌套容器

    • 创建外部容器时,需要指定内部容器的类型。
    • 向外部容器中添加元素时,每个元素都是一个内部容器的实例。
    • 可以通过外部容器的迭代器来遍历外部容器,并通过内部容器的迭代器来访问和操作内部容器中的元素。

        下面是一个简单的示例代码,演示了如何在vector容器中嵌套vector容器,并对嵌套容器进行操作:

        在这个示例代码中,nestedVector是一个vector容器,其中每个元素都是一个vector<int>类型的容器。通过将内部vector容器作为外部vector容器的元素,实现了嵌套容器的效果。通过两层循环,可以遍历外部容器和内部容器,并访问内部容器中的元素。

        嵌套容器提供了一种组织数据的方式,特别适用于需要多维数据结构的场景,如二维矩阵、多级索引等。

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

int main() {
    // 定义外部容器,元素类型是内部容器
    vector<vector<int>> nestedVector;

    // 添加内部容器到外部容器中
    nestedVector.push_back(vector<int>{1, 2, 3});
    nestedVector.push_back(vector<int>{4, 5});
    nestedVector.push_back(vector<int>{6, 7, 8, 9});

    // 遍历外部容器并访问内部容器中的元素
    for (const auto& innerVec : nestedVector) {
        for (int num : innerVec) {
            cout << num << " ";
        }
        cout << endl;
    }

    return 0;
}

        下面给出代码详细分析一下应用过程:这段代码演示了在vector容器中嵌套另一个vector容器的情况。下面是对代码的简要解释:

  1. 定义嵌套容器

    • 创建了一个外部vector<vector<int>>类型的容器v,其中每个元素都是一个内部vector<int>类型的容器。
    • 同时,定义了四个内部vector<int>类型的容器v1v2v3v4
  2. 操作内部容器

    • 在内部容器中,使用push_back方法向每个容器中插入一些整数数据。
  3. 将内部容器添加到外部容器

    • 将四个内部容器v1v2v3v4添加到外部容器v中,这样外部容器中就包含了四个内部容器。
  4. 遍历嵌套容器

    • 使用外部容器的迭代器遍历外部容器,得到内部容器。
    • 对于每个内部容器,再次使用内部容器的迭代器遍历,输出其中的元素。

        通过这段代码,展示了如何在vector容器中嵌套另一个vector容器,并且使用迭代器对嵌套容器进行遍历和访问。

#include <iostream>
using namespace std;
#include<vector>
#include<string>


// 容器嵌套容器


void test01()
{
	vector<vector<int>>v;   // 这里就相当于在大容器中,又加入了小容器,为int类型的数组

	// 在定义小容器
	vector<int>v1;
	vector<int>v2;
	vector<int>v3;
	vector<int>v4;
	// 小容器中插入数据   这一步是向小容器中插入数据,
	for (int i = 0; i < 4;i++)
	{
		v1.push_back(i + 1);
		v2.push_back(i + 2);
		v3.push_back(i + 3);
		v4.push_back(i + 4);
	}
	// 将容器元素插入到大容器中   这一步大容器中也需要有数据
	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	v.push_back(v4);

	// 使用大容器遍历小容器
	for (vector<vector<int>>::iterator it = v.begin(); it != v.end();it++)
	{
		// 这个时候可以看出来 *it 返回的是一个vector<int> 也就是有时一个容器,因此还需要一次遍历
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end();vit++)
		{
			cout << *vit << " ";
		}
		cout << endl;

	}


}

int main() {

	test01();
	system("pause");
	return 0;
}

        实例结果如下图所示: 

总结

     STL(Standard Template Library,标准模板库)是 C++ 标准库的一部分,提供了一组通用的模板类和函数,用于实现常见的数据结构和算法。以下是关于STL基础知识的总结:

  1. 容器(Containers)

    • 向量(Vector):动态数组,支持快速随机访问和在末尾插入元素。
    • 链表(List):双向链表,支持在任意位置进行插入和删除操作。
    • 队列(Queue):先进先出的队列。
    • 栈(Stack):后进先出的栈。
    • 映射(Map):关联数组,存储键-值对,并根据键快速查找值。
  2. 迭代器(Iterators)

    • 迭代器用于遍历容器中的元素,提供了统一的访问容器元素的接口。
    • 包括输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器。
  3. 算法(Algorithms)

    • STL提供了各种算法,如排序、查找、变换和合并等。
    • 可以通过算法与迭代器结合对容器进行各种操作。
    • 一些常见算法包括sortfindtransformmerge等。
  4. 函数对象(Functors)

    • 函数对象是重载了operator()的类,可以像函数一样调用。
    • 可以自定义函数对象,并将其用于算法中,比如排序或查找。
  5. 适配器(Adapters)

    • 堆栈适配器(Stack Adapter):基于vectordeque实现的栈。
    • 队列适配器(Queue Adapter):基于dequelist实现的队列。
  6. 分配器(Allocators)

    • 分配器用于管理内存分配。
    • 可以自定义分配器并用于 STL 容器。
  7. 内置类型(Basic Types)

    • STL包含了一些内置类型,如pair用于存储一对值、tuple用于存储任意数量的值。

        总的来说,STL提供了一组灵活、高效且通用的工具,能够帮助我们更方便地实现各种数据结构和算法。熟练掌握STL的基础知识对于C++编程是非常重要的。   

  • 32
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一伦明悦

感谢,您的支持是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值