前言:
模板初阶 | STL简介
文章目录
一、模板初阶1.1 函数模板1.2 类模板 二、STL简介 (了解)
一、模板初阶
泛式编程(Generic Programming)指的是一种编程范式,其核心思想是编写可以在不同数据类型上通用的代码,从而提高代码的复用性、可维护性和可扩展性。
泛式编程的实现方式包括模板(Template)和泛型(Generics)。在C++中,使用模板可以实现泛型编程,而在Java、C#等语言中,则使用泛型来实现类似的功能。
1.1 函数模板
函数模板的格式如下:
template <typename T1,typename T2,......,typename Tn>返回类型 函数名(参数列表) { // 函数体}
template <typename T1,typename T2,......,typename Tn>
:以关键字 template
开始,后跟尖括号 < >
,里面是一个或多个模板参数的列表。例如typename T1
定义了一个类型模板参数 T1
,可以替换为其他任意合法的标识符。
返回类型
:函数的返回类型,可以是任意类型。
函数名
:函数的名称,可以是任意合法的标识符。
参数列表
:函数的参数列表,可以包含零个或多个参数,每个参数可以是任意合法的标识符,也可以是模板参数。
函数体
:函数的具体实现,包括函数的操作和逻辑。
一个具体的函数模板示例:
// 函数模板的定义template <typename T>T maximum(T x, T y) { return (x > y) ? x : y;}// 调用模板函数int result1 = maximum(10, 20);double result2 = maximum(3.14, 2.71);
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
函数模板实例化
函数模板的实例化是指根据具体的类型,生成模板函数的过程。当编译器在代码中遇到对函数模板的调用时,它会根据传递的参数类型推导出模板参数的具体类型,并生成相应的函数实例。
// 定义函数模板template <typename T>T maximum(T x, T y) { return (x > y) ? x : y;}int main() { int result1 = maximum(10, 20); // 实例化为 maximum<int>(10, 20),T 被推导为 int double result2 = maximum(3.14, 2.71); // 实例化为 maximum<double>(3.14, 2.71),T 被推导为 double //或者我们可以手动进行实例化 // 手动实例化模板函数为 int 类型 int result3 = maximum<int>(10, 20); // 手动实例化模板函数为 double 类型 double result4 = maximum<double>(3.14, 2.71); return 0;}
函数模板的实例化是在编译时进行的,每次调用函数模板时,都会根据传递的参数类型生成相应的函数实例。这样,同一个函数模板可以用于多种不同类型的参数,实现了通用的函数。
模板参数的匹配原则
一个非模板函数和一个同名的函数模板可以同时存在,编译器会根据一定的匹配规则来确定使用哪一个函数。
例如:
// 非模板函数void foo(int x) { cout << "Non-template function: " << x << endl;}// 函数模板template <typename T>void foo(T x) { cout << "Function template: " << x << endl;}int main() { foo(42); // 调用非模板函数 foo("hello"); // 调用函数模板 return 0;}
在这个例子中,调用 foo(42)
时会匹配到非模板函数 foo(int)
,因为参数类型 int
精确匹配。而调用 foo("hello")
时会匹配到函数模板 foo(T)
,并实例化为 foo(const char*)
,因为函数模板的模板参数可以匹配到参数类型 const char*
。
1.2 类模板
除函数模板外,类模板是C++中另一个重要的模板形式,允许定义通用的类,其中的某些成员类型或成员函数可以由用户指定。类模板以 template <typename T>
或 template <class T>
开始,后跟着类的定义,其中 T 是一个占位符类型,表示任意类型。
例如我们创建一个类,类的对象的类型可能是int
或者char
等等:
template <class T, int N>class Stack {private: T elements[N]; int top; // 栈顶索引public: Stack() : top(-1) {} // 初始化栈顶索引为-1 void push(const T& element) { if (top == N - 1) { // 栈满,抛异常 throw std::overflow_error("Stack<>::push(): stack overflow"); } elements[++top] = element; // 将元素入栈 }};
这个示例中,Stack
是一个类模板,有两个模板参数:T
表示元素的类型,N
表示栈的最大容量。
//类模板实例化Stack<int, 10> intStack; // 创建一个最大容量为 10 的整型栈Stack<double, 5> doubleStack; // 创建一个最大容量为 5 的双精度浮点型栈
当类模板中的函数定义放在类外部时,如果函数使用了类模板的模板参数,那么函数定义必须在其前面加上模板参数列表,并且在函数名之后使用类模板的模板参数,以表明该函数是属于类模板的。
例如:
template <typename T>class Stack {public: void push(const T& element); // 函数声明};// 函数定义放在类外部template <typename T>void Stack<T>::push(const T& element) { // 函数定义}
二、STL简介 (了解)
STL 是 C++ 标准模板库(Standard Template Library)的缩写。它是 C++ 标准库的一部分,提供了一系列的通用模板类和函数,用于实现各种常见的数据结构和算法,例如向量(vector)、链表(list)、栈(stack)、队列(queue)、集合(set)、映射(map)等,以及对这些数据结构进行操作的算法,如排序、查找、遍历等。
STL 的设计思想是基于泛型编程(Generic Programming),它使用模板技术实现通用性和灵活性,使得用户能够在不同的数据类型上使用相同的算法和数据结构。STL 提供了大量的模板类和函数,使得程序员可以在开发过程中更加高效地处理各种数据结构和算法问题。
STL(Standard Template Library)的六大组件:
容器(Containers):
容器是用于存储和管理数据的数据结构,包括向量(vector)、链表(list)、双端队列(deque)、队列(queue)、栈(stack)、集合(set)、映射(map)等。每种容器都有不同的特点和适用场景,可以根据具体的需求选择合适的容器。
算法(Algorithms):
算法是用于对容器中的元素进行操作和处理的函数,包括排序、查找、遍历、拷贝、删除等一系列操作。STL提供了大量的算法,能够满足各种不同的需求,提高代码的复用性和可读性。
迭代器(Iterators):
迭代器是一种用于遍历容器中元素的对象,它提供了统一的访问接口,使得算法能够与容器解耦合。STL提供了多种类型的迭代器,包括输入迭代器、输出迭代器、正向迭代器、双向迭代器和随机访问迭代器,每种迭代器都有不同的功能和特点。
仿函数(Functors):
仿函数是一种可调用对象,它可以像函数一样被调用,并且可以作为算法的参数使用。仿函数可以是普通函数指针、函数对象(重载了函数调用运算符 operator()
的类对象)、Lambda 表达式等。STL提供了一些内置的仿函数,同时也支持用户自定义的仿函数。
适配器(Adapters):
适配器是一种用于在不同容器之间进行转换和包装的机制,它可以将一个容器转换为另一个容器,或者在一个容器的基础上提供新的功能。STL提供了一些常用的适配器,如栈适配器(stack)、队列适配器(queue)、优先队列适配器(priority_queue)等。
配置器(Allocators):
配置器是一种用于内存管理的机制,它控制着容器在内存中的分配和释放。STL中的容器和算法都使用了配置器来进行内存管理,但配置器的具体实现通常是由编译器提供的默认配置器。STL也允许用户自定义配置器,以满足特定的需求。
这些六大组件构成了STL的核心部分,它们共同组成了一个功能强大、高效易用的库,为C++程序员提供了丰富的数据结构和算法,极大地提高了程序开发的效率和质量。
如果你喜欢这篇文章,点赞?+评论+关注⭐️哦!
欢迎大家提出疑问,以及不同的见解。