前言
通过对C++的特性,类和对象的学习和C++的内存管理对C++基本上有了全面的认识,但是C++的核心在于
STL
一、STL
简介
什么是STL
- C++
STL
(Standard Template Library,标准模板库)是C++编程语言中一个功能强大的模板库,它提供了一系列通用的数据结构和算法。 STL
的设计基于泛型编程,这意味着它使用模板来编写独立于任何特定数据类型的代码。
STL
的核心组件包括容器(如向量、链表、集合等)、迭代器、算法、函数对象和适配器等。- 这些组件使得程序员能够以简洁、高效的方式处理数据结构和算法问题,减少了编码工作量,提高了代码的可重用性和性能
STL
版本
HP STL
:由AlexanderStepanov
和Meng Lee
在惠普实验室完成的原始版本,是所有STL实现版本的始祖。PJ STL
:由P. J. Plauge
r开发,继承自HP版本,被Windows Visual C++采用。SGI STL
:由Silicon Graphics Computer Systems, Inc公司开发,继承自HP版本,被GCC
(Linux)采用,可移植性好,可公开、修改甚至贩卖。
STLport
:由Boris Fomitchev
开发,继承自SGI STL
,旨在提供一个可移植到不同平台的STL
版本
STL
为什么可以成为C++标准库的一部分
- 泛型编程支持:
STL
利用C++的模板机制实现了泛型编程,使得容器和算法能够处理不同类型的数据,提高了代码的重用性和灵活性。 ! - 高效的数据结构:
STL
提供了多种容器类,如向量(vector)、链表(list)、双端队列(deque)、集合(set)、映射(map)等,这些容器具有高效的数据存储和访问方式,并且可以方便地进行插入、删除和查找等操作。 - 统一的迭代器接口:
STL
的容器和算法都使用迭代器来访问和操作元素,迭代器提供了统一的接口,使得算法可以独立于具体的数据类型进行操作 - 丰富的算法库:
STL
提供了一系列算法库,包括排序、查找、合并、堆操作、数值算法等,这些算法都被实现为函数模板,可以方便地应用于不同的容器和数据类型。 - 函数对象的使用:
STL
的算法库中使用了函数对象(Functor)来封装操作,通过函数对象,STL
可以将自定义的操作应用于算法中,提高了代码的可定制性和扩展性。 - 自动内存管理:
STL
的容器类和算法库都采用了自动的内存管理机制,有效避免了内存泄漏和访问越界等问题。
STL
六大组件
二、模板
模板特性
- 函数模板特化:当需要为特定类型提供不同于模板定义的函数行为时,可以使用函数模板特化。例如,对于内置类型
int
,可能需要一个优化过的打印函数,而对于其他类型则使用通用模板定义。 - 类模板特化:类模板特化允许为特定类型或类型组合提供定制化的类成员函数或数据成员。这在需要为特定类型提供特定的类行为时非常有用。
- 模板偏特化:模板偏特化是模板特化的一种形式,它允许部分参数的定制化,而保留其他参数的泛化行为。这在处理具有共同基类或接口的不同派生类型时特别有用。
- 全特化:全特化是指为模板提供一个具体类型的实现,这样编译器在遇到该具体类型时会直接使用特化版本,而不是泛化版本。这有助于提高代码的效率和减少编译时间。
- 参数修饰特化:参数修饰特化是通过改变模板参数的类型或数量来实现特化的技术。这可以用来解决模板参数匹配的歧义或提供额外的功能。
- 非类型模板参数特化:非类型模板参数特化允许为特定的非类型参数值提供定制化的模板实现,这在需要根据不同的常量大小或值来调整模板行为时非常有用
泛型编程
在学习C语言的时候如果实现两个数交换 比如:整形,浮点型,字符,会写三个函数。
void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } void Swap(double& left, double& right) { double temp = left; left = right; right = temp; } void Swap(char& left, char& right) { char temp = left; left = right; right = temp; }
这样重复性的代码,是很多人讨厌的地方,正因为这个出现了泛型编程。以上的代码一个用一个函数模板替代,交给编译器实现。
template<class T> void Swap(T& a, T& b) { int tmp = a; a = b; b = tmp; }
- 在实现整形交换的时候传整形,实现浮点型减缓的时候传浮点型。
函数模板的的原理
- 传不同的类型编译器会自动生成不同类型的函数。
//在调用这三个函数的时候编译器底层调用的函数地址是不样的 int a = 1; int b = 2; int c = 1.0; int d = 2.0; int e = 'e'; int f = 'f'; Swap(a, b); Swap(c, d); Swap(e, f); 23: Swap(a, b); 00007FF760C019EE lea rdx,[b] 00007FF760C019F2 lea rcx,[a] 00007FF760C019F6 call Swap<int> (07FF760C012F8h) 00007FF760C019FB nop 24: Swap(c, d); 00007FF760C019FC lea rdx,[d] 00007FF760C01A00 lea rcx,[c] 00007FF760C01A04 call Swap<int> (07FF760C012F8h) 00007FF760C01A09 nop 25: Swap(e, f); 00007FF760C01A0A lea rdx,[f] 00007FF760C01A11 lea rcx,[e] 00007FF760C01A18 call Swap<int> (07FF760C012F8h) 00007FF760C01A1D nop
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模 板就是将本来应该我们做的重复的事情交给了编译器
类模板
类模板是一种强大的编程技术,它允许开发者定义泛型类,这些类可以在创建时指定不同的数据类型。
类模板的定义
- 关键字
template
:用于声明模板的开始。 - 模板参数列表:位于尖括号
< >
内,定义了模板的类型参数或非类型参数。类型参数通常使用typename
或class
关键字声明。 - 类模板名:紧随模板参数列表之后,是模板的名称。
- 类模板体:包含类模板的成员声明,可以是类的定义、函数定义或类型别名等。
template <typename T> class MyClass { public: T member; MyClass(T initialValue) : member(initialValue) {} // 其他成员函数和类型定义 };
模板的实例化
- 函数模板实例化是指编译器根据函数模板和具体类型的实参生成特定类型的函数定义的过程。
隐式实例化
- 隐式实例化是编译器在遇到函数模板调用时自动进行的实例化过程。编译器根据函数调用的实参类型来推导模板参数的实际类型,并生成相应的模板函数实例。
例如,如果有一个模板函数 template <typename T> void f(T a, T b)
,当调用 f(1, 2)
时,编译器会自动实例化一个 f<int>(int, int)
的函数版本。
//写出一个模板 template<class T> void Swap(T& a, T& b) { int tmp = a; a = b; b = tmp; } //在调用f(1,1),会生成 int swap(int& a,int& b) int swap(int& a,int b) { int tmp = a; a = b; b = tmp; }
显示实例化
- 显式实例化是指程序员在代码中明确指定模板实例化的具体类型,以便提前生成相应的模板函数实例。
//写出一个模板 template<class T> void Swap(T& a, T& b) { int tmp = a; a = b; b = tmp; } //在调用f<int>(1.0,1.0),会生成 int swap(float& a,float& b) int swap(float& a,float& b) { int tmp = a; a = b; b = tmp; }