相关文章:
从0开始C++(四):作用域限定符、this指针、static与const关键字
目录
一、模板
模板是一种泛型编程的工具,它允许在编译时对代码进行参数化。使用模板可以编写通用的代码,可以适用于多种不同类型的数据,使得代码可以更加通用和灵活。通过使用模板,可以减少代码重复,提高代码的可重用性和可维护性。
模板的语法使用尖括号 < > 来定义模板参数,参数可以是类型参数或非类型参数。类型参数可以用来指定函数或类的参数类型,非类型参数可以用来指定常量或枚举值。
使用模板时,需要在每个参数前加上关键字 template ,并用尖括号< >将参数包裹起来。
C++模板由两种类型:函数模板和类模板。
函数模板
函数模板是一种让函数能够根据不同参数类型进行通用操作的方法。通过使用函数模板,可以在不重复编写相似函数的情况下,对不同类型的数据进行相同的操作。
实例
#include <iostream>
using namespace std;
// 模板函数
template<typename T> // 可以是class也可以是typename
T add(T a,T b)
{
return a+b;
}
int main()
{
string str = "hello ";
string str2 = "world";
cout << add(12,32) << endl; // 44
cout << add(str,str2) << endl; //hello world
return 0;
}
类模板
类模板是一种允许定义通用类的方法。类模板可以定义一个模板类,该类可以根据不同的参数类型生成不同的具体类。这样可以避免为每个具体类型编写单独的类。
实例
#include <iostream>
using namespace std;
// 模板类
template<class T>
class Test
{
private:
T val;
public:
Test(T v):val(v){}
T get_val()const
{
return val;
}
void set_val(const T &val)
{
this->val = val;
}
};
int main()
{
Test<int> t1(20);
cout << t1.get_val() << endl;
t1.set_val(10);
cout << t1.get_val() << endl; // 10
Test<double> t2(0.2);
cout << t2.get_val() << endl; // 0.2
t2.set_val(20.3);
cout << t2.get_val() << endl;
return 0;
}
二、容器
标准模板类STL
标准模板库(Standard Template Library,STL)是惠普实验室开发的一系列软件的统称。虽说它主要出现到了C++中,但是在被引入C++之前该技术就已经存在了很长时间。
STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
容器的概念
容器是用来存储数据的集合,数据元素可以是任何类型(因为是使用模板实现)。C++标准库提供了多种容器类,每个容器类都有其特定的特点和适用场景。
常见的C++容器类包括:
-
数组 (
array
):固定大小的连续存储空间,可以快速访问元素。 -
向量 (
vector
):动态数组,可以根据需要调整大小,支持快速随机访问和在尾部插入/删除元素。 -
列表 (
list
):双向链表,可以在任意位置插入/删除元素,但随机访问效率较低。 -
前向列表 (
forward_list
):单向链表,与list
类似,但删除操作效率更高。 -
堆栈 (
stack
):后进先出 (LIFO) 的数据结构,只能在顶部插入/删除元素。 -
队列 (
queue
):先进先出 (FIFO) 的数据结构,只能在尾部插入,在头部删除元素。 -
双端队列 (
deque
):双向队列,可以在两端插入/删除元素,支持随机访问。 -
集合 (
set
):有序的唯一元素集合,可以快速插入/删除元素,并支持快速查找。 -
映射 (
map
):键-值对的集合,按照键的顺序进行排序,可以快速插入/删除元素,并支持以键进行查找。 -
哈希集合 (
unordered_set
):无序的唯一元素集合,使用哈希函数实现,插入/删除/查找操作的平均时间复杂度为常数。 -
哈希映射 (
unordered_map
):无序的键-值对集合,使用哈希函数实现,插入/删除/查找操作的平均时间复杂度为常数。
这些容器类提供了丰富的接口和功能,可以根据不同的需求选择合适的容器。另外,C++标准库还提供了算法库,可以方便地对容器中的元素进行各种操作和处理。
数组(array)
array
是一种固定大小的连续内存空间,用于存储相同类型的对象。可以使用array
类模板来创建数组,以下是array
数组的常用操作方法:
1、创建array
数组:
#include <array>
std::array<int, 5> myArray; // 创建一个具有5个整数元素的数组
2、获取数组大小:
std::array<int, 5> myArray;
int size = myArray.size(); // 获取数组的大小,返回值为5
3、访问数组元素:
std::array<int, 5> myArray;
myArray[0] = 10; // 使用下标操作符访问和修改数组元素
int value = myArray[1]; // 获取数组的第2个元素的值
4、数组赋值和初始化:
std::array<int, 5> myArray;
myArray = {1, 2, 3, 4, 5}; // 使用花括号列表进行赋值
std::array<int, 5> myArray2 = {6, 7, 8, 9, 10}; // 使用花括号列表进行初始化
5、数组迭代器操作:
std::array<int, 5> myArray = {1, 2, 3, 4, 5};
for (auto it = myArray.begin(); it != myArray.end(); ++it) {
// 使用迭代器遍历数组元素
std::cout << *it << " ";
}
6、判断数组是否为空:
std::array<int, 5> myArray;
bool isEmpty = myArray.empty(); // 如果数组为空,则返回true,否则返回false
7、获取数组首尾元素的引用:
std::array<int, 5> myArray = {1, 2, 3, 4, 5};
int& frontElement = myArray.front(); // 获取数组第一个元素的引用
int& backElement = myArray.back(); // 获取数组最后一个元素的引用
8、交换数组内容:
std::array<int, 5> myArray = {1, 2, 3, 4, 5};
std::array<int, 5> myArray2 = {6, 7, 8, 9, 10};
myArray.swap(myArray2); // 交换两个数组的内容
向量 (vector
)
vector
是一种动态数组,它可以自动调整大小,并且具有很多方便的操作方法。以下是vector
向量的常用操作方法:
1、创建vector
向量:
#include <vector>
std::vector<int> myVector; // 创建一个整数向量
2、获取向量大小:
std::vector<int> myVector;
int size = myVector.size(); // 获取向量的大小
3、向向量中添加元素:
std::vector<int> myVector;
myVector.push_back(10); // 在向量尾部添加一个元素
4、访问向量元素:
std::vector<int> myVector;
myVector[0] = 10; // 使用下标操作符访问和修改向量元素
int value = myVector[1]; // 获取向量的第2个元素的值
5、向量赋值和初始化:
std::vector<int> myVector;
myVector = {1, 2, 3, 4, 5}; // 使用花括号列表进行赋值
std::vector<int> myVector2 = {6, 7, 8, 9, 10}; // 使用花括号列表进行初始化
6、向量迭代器操作:
std::vector<int> myVector = {1, 2, 3, 4, 5};
for (auto it = myVector.begin(); it != myVector.end(); ++it) {
// 使用迭代器遍历向量元素
std::cout << *it << " ";
}
7、判断向量是否为空:
std::vector<int> myVector;
bool isEmpty = myVector.empty(); // 如果向量为空,则返回true,否则返回false
8、获取向量首尾元素的引用:
std::vector<int> myVector = {1, 2, 3, 4, 5};
int& frontElement = myVector.front(); // 获取向量第一个元素的引用
int& backElement = myVector.back(); // 获取向量最后一个元素的引用
9、删除向量中的元素:
std::vector<int> myVector = {1,
列表 (list
)
list
是一种双向链表,它可以动态地插入和删除元素,并且具有很多方便的操作方法。以下是list
列表的一些常用操作方法:
1、创建list
列表:
#include <list>
std::list<int> myList; // 创建一个整数列表
2、获取列表大小:
std::list<int> myList;
int size = myList.size(); // 获取列表的大小
3、向列表中添加元素:
std::list<int> myList;
myList.push_back(10); // 在列表尾部添加一个元素
myList.push_front(20); // 在列表头部添加一个元素
4、访问列表元素:
std::list<int> myList;
myList.front() = 10; // 获取和修改列表的第一个元素
int value = myList.back(); // 获取列表的最后一个元素的值
5、列表赋值和初始化:
std::list<int> myList;
myList = {1, 2, 3, 4, 5}; // 使用花括号列表进行赋值
std::list<int> myList2 = {6, 7, 8, 9, 10}; // 使用花括号列表进行初始化
6、列表迭代器操作:
std::list<int> myList = {1, 2, 3, 4, 5};
for (auto it = myList.begin(); it != myList.end(); ++it) {
// 使用迭代器遍历列表元素
std::cout << *it << " ";
}
7、判断列表是否为空:
std::list<int> myList;
bool isEmpty = myList.empty(); // 如果列表为空,则返回true,否则返回false
8、删除列表中的元素:
std::list<int> myList = {1, 2, 3, 4, 5};
myList.pop_back(); // 删除列表尾部的元素
myList.pop_front(); // 删除列表头部的元素
9、插入元素到指定位置:
std::list<int> myList = {1, 2, 3, 4, 5};
auto it = std::find(myList.begin(), myList.end(), 3); // 找到值为3的元素的迭代器
myList.insert(it, 10); // 在指定位置插入一个元素
10、移除指定值的元素:
std::list<int> myList = {1, 2, 3, 4, 5};
myList.remove(3); // 移除所有值为3的元素
队列 (queue
)
queue
是一种队列容器,它遵循先进先出(First-In-First-Out,FIFO)的原则。以下是queue
队列的一些常用操作方法:
1、创建queue
队列:
#include <queue>
std::queue<int> myQueue; // 创建一个整数队列
2、获取队列大小:
std::queue<int> myQueue;
int size = myQueue.size(); // 获取队列的大小
3、向队列尾部添加元素:
std::queue<int> myQueue;
myQueue.push(10); // 在队列尾部添加一个元素
4、从队列头部移除元素:
std::queue<int> myQueue;
myQueue.pop(); // 移除队列头部的元素
5、获取队列头部元素的值:
std::queue<int> myQueue;
int frontValue = myQueue.front(); // 获取队列头部元素的值
6、获取队列尾部元素的值:
std::queue<int> myQueue;
int backValue = myQueue.back(); // 获取队列尾部元素的值
7、判断队列是否为空:
std::queue<int> myQueue;
bool isEmpty = myQueue.empty(); // 如果队列为空,则返回true,否则返回false
8、清空队列中的元素:
std::queue<int> myQueue;
myQueue = {}; // 清空队列中的所有元素
双端队列 (deque
)
在C++中,deque
(双端队列)是一种支持在头部和尾部进行插入和删除操作的动态数组。以下是deque
的一些常用操作方法:
1、创建deque
双端队列:
#include <deque>
std::deque<int> myDeque; // 创建一个整数双端队列
2、获取双端队列大小:
std::deque<int> myDeque;
int size = myDeque.size(); // 获取双端队列的大小
3、向双端队列头部插入元素:
std::deque<int> myDeque;
myDeque.push_front(10); // 在双端队列头部插入一个元素
4、向双端队列尾部插入元素:
std::deque<int> myDeque;
myDeque.push_back(20); // 在双端队列尾部插入一个元素
5、从双端队列头部删除元素:
std::deque<int> myDeque;
myDeque.pop_front(); // 删除双端队列头部的元素
6、从双端队列尾部删除元素:
std::deque<int> myDeque;
myDeque.pop_back(); // 删除双端队列尾部的元素
7、获取双端队列头部元素的值:
std::deque<int> myDeque;
int frontValue = myDeque.front(); // 获取双端队列头部元素的值
8、获取双端队列尾部元素的值:
std::deque<int> myDeque;
int backValue = myDeque.back(); // 获取双端队列尾部元素的值
9、判断双端队列是否为空:
std::deque<int> myDeque;
bool isEmpty = myDeque.empty(); // 如果双端队列为空,则返回true,否则返回false
10、清空双端队列中的元素:
std::deque<int> myDeque;
myDeque.clear(); // 清空双端队列中的所有元素
集合 (set
)
set
(集合)是一种基于红黑树实现的有序集合容器,它存储唯一的元素,并且按照升序排序。以下是set
的一些常用操作方法:
1、创建一个set
集合:
#include <set>
std::set<int> mySet; // 创建一个整数集合
2、向集合中插入元素:
std::set<int> mySet;
mySet.insert(10); // 插入一个元素
3、从集合中删除元素:
std::set<int> mySet;
mySet.erase(10); // 删除指定元素
4、检查集合中是否存在元素:
std::set<int> mySet;
bool contains = mySet.count(10); // 如果集合中存在元素10,则返回true,否则返回false
5、获取集合中的元素个数:
std::set<int> mySet;
int size = mySet.size(); // 返回集合中元素的个数
6、遍历集合:
std::set<int> mySet;
for (const auto& element : mySet) {
// 处理每个元素
}
7、判断集合是否为空:
std::set<int> mySet;
bool isEmpty = mySet.empty(); // 如果集合为空,则返回true,否则返回false
8、清空集合中的元素:
std::set<int> mySet;
mySet.clear(); // 清空集合中的所有元素
映射 (map
)
map
是一种基于红黑树实现的键值对映射容器,它存储唯一的键和对应的值,并根据键的升序进行排序。以下是map
的一些常用操作方法:
1、创建一个map
映射:
#include <map>
std::map<int, std::string> myMap; // 创建一个整数到字符串的映射
2、向映射中插入键值对:
std::map<int, std::string> myMap;
myMap.insert(std::make_pair(1, "one")); // 插入一个键值对
3、从映射中删除键值对:
std::map<int, std::string> myMap;
myMap.erase(1); // 删除指定键的键值对
4、访问映射中的值:
std::map<int, std::string> myMap;
std::string value = myMap[1]; // 获取键为1的值
5、检查映射中是否存在键:
std::map<int, std::string> myMap;
bool contains = myMap.count(1); // 如果映射中存在键1,则返回true,否则返回false
6、获取映射中的键值对个数:
std::map<int, std::string> myMap;
int size = myMap.size(); // 返回映射中键值对的个数
7、遍历映射中的键值对:
std::map<int, std::string> myMap;
for (const auto& pair : myMap) {
int key = pair.first;
std::string value = pair.second;
// 处理每个键值对
}
8、判断映射是否为空:
std::map<int, std::string> myMap;
bool isEmpty = myMap.empty(); // 如果映射为空,则返回true,否则返回false
9、清空映射中的键值对:
std::map<int, std::string> myMap;
myMap.clear(); // 清空映射中的所有键值对
哈希集合 (unordered_set
)
unordered_set
是一种基于哈希表实现的集合容器,它存储唯一的值,并根据哈希值进行快速查找。以下是unordered_set
的一些常用操作方法:
1、创建一个unordered_set
哈希集合:
#include <unordered_set>
std::unordered_set<int> mySet; // 创建一个整数类型的哈希集合
2、向哈希集合中插入元素:
std::unordered_set<int> mySet;
mySet.insert(1); // 插入一个元素
3、从哈希集合中删除元素:
std::unordered_set<int> mySet;
mySet.erase(1); // 删除指定的元素
4、检查哈希集合中是否存在元素:
std::unordered_set<int> mySet;
bool contains = mySet.count(1); // 如果集合中存在元素1,返回true,否则返回false
5、获取哈希集合的大小:
std::unordered_set<int> mySet;
int size = mySet.size(); // 返回集合中元素的个数
6、遍历哈希集合的元素:
std::unordered_set<int> mySet;
for (const auto& element : mySet) {
// 处理每个元素
}
7、判断哈希集合是否为空:
std::unordered_set<int> mySet;
bool isEmpty = mySet.empty(); // 如果集合为空,返回true,否则返回false
8、清空哈希集合中的元素:
std::unordered_set<int> mySet;
mySet.clear(); // 清空集合中的所有元素
哈希映射 (unordered_map
)
unordered_map
是一种基于哈希表实现的映射容器,它存储键值对,并根据键的哈希值进行快速查找。以下是unordered_map
的一些常用操作方法:
1、创建一个unordered_map
哈希映射:
#include <unordered_map>
std::unordered_map<std::string, int> myMap; // 创建一个键为字符串,值为整数的哈希映射
2、向哈希映射中插入键值对:
std::unordered_map<std::string, int> myMap;
myMap.insert({"apple", 10}); // 插入一个键为"apple",值为10的键值对
myMap["banana"] = 20; // 插入一个键为"banana",值为20的键值对
3、从哈希映射中删除键值对:
std::unordered_map<std::string, int> myMap;
myMap.erase("apple"); // 删除键为"apple"的键值对
4、访问和修改哈希映射中的值:
std::unordered_map<std::string, int> myMap;
int value = myMap["apple"]; // 获取键为"apple"的值
myMap["banana"] = 30; // 修改键为"banana"的值为30
5、检查哈希映射中是否存在键:
std::unordered_map<std::string, int> myMap;
bool contains = myMap.count("apple"); // 如果哈希映射中存在键"apple",返回true,否则返回false
6、获取哈希映射的大小:
std::unordered_map<std::string, int> myMap;
int size = myMap.size(); // 返回哈希映射中键值对的个数
7、遍历哈希映射的键值对:
std::unordered_map<std::string, int> myMap;
for (const auto& pair : myMap) {
std::string key = pair.first;
int value = pair.second;
// 处理每个键值对
}
8、判断哈希映射是否为空:
std::unordered_map<std::string, int> myMap;
bool isEmpty = myMap.empty(); // 如果哈希映射为空,返回true,否则返回false
9、清空哈希映射中的键值对:
std::unordered_map<std::string, int> myMap;
myMap.clear(); // 清空哈希映射中的所有键值对
三、迭代器
迭代器是一种用于遍历和访问容器中元素的对象。迭代器提供了一种通用的访问容器元素的方式,使得我们可以在不依赖具体容器类型的情况下进行遍历和操作。迭代器分为正向迭代器和反向迭代器两种类型,分别用于正向遍历和反向遍历容器。
1、迭代器的定义和获取: 迭代器可以通过容器的成员函数begin()
和end()
来获取。其中,begin()
返回指向容器中第一个元素的迭代器,end()
返回指向容器尾部的迭代器。
std::vector<int> myVector = {1, 2, 3, 4, 5};
std::vector<int>::iterator it;
for (it = myVector.begin(); it != myVector.end(); ++it) {
std::cout << *it << " ";
}
2、使用迭代器访问容器元素: 通过解引用操作符*
可以获取迭代器指向的元素的值。
std::vector<int> myVector = {1, 2, 3, 4, 5};
std::vector<int>::iterator it = myVector.begin();
std::cout << *it << std::endl; // 输出:1
3、使用迭代器修改容器元素: 通过解引用操作符*
可以获取迭代器指向的元素的引用,从而可以修改容器中的元素。
std::vector<int> myVector = {1, 2, 3, 4, 5};
std::vector<int>::iterator it = myVector.begin();
*it = 10; // 修改第一个元素的值
4、迭代器的移动: 迭代器可以通过自增(++
)或自减(--
)来移动到容器中的下一个或上一个元素。
std::vector<int> myVector = {1, 2, 3, 4, 5};
std::vector<int>::iterator it = myVector.begin();
++it; // 移动到下一个元素
--it; // 移动到上一个元素
5、迭代器的比较: 使用比较操作符(==
、!=
等)可以比较两个迭代器的相对位置。
std::vector<int> myVector = {1, 2, 3, 4, 5};
std::vector<int>::iterator it1 = myVector.begin();
std::vector<int>::iterator it2 = myVector.end();
if (it1 == it2) {
std::cout << "The iterators are equal." << std::endl;
} else {
std::cout << "The iterators are not equal." << std::endl;
}
需要注意的是,在使用迭代器时,要确保迭代器指向的元素是存在的,避免越界访问。此外,当容器发生改变(元素的插入、删除等操作)时,迭代器可能会失效,因此在使用迭代器之前要先判断其是否有效。