C++中模板和STL介绍详解

目录
  • 一、模板
    • 1.1.函数模板
      • 1.1.1.两种函数模板的实例化
      • 1.1.2.模板参数的匹配原则
    • 1.2.类模板
  • 二、STL
  • 总结

一、模板

对于一个交换函数,虽然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;
 }

但是依然有不足的地方,比如如果我们要交换其他类型,比如char或者类类型,那还是得再写一个交换函数,这样原来写好的其他类型的交换函数就没有复用起来,大大降低了效率。

因此,C++引入了模板的概念,通过模板,即可实现一份代码交换不同数据。

模板,其实就是告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码。

1.1.函数模板

**泛型编程:**在之前,函数都是针对某个具体的类型(比如int,char),而泛型则是针对一个广泛的类型。模板则是泛型编程的基础。

所以函数模板的参数并不是一个具体的类型,只有当调用时才能确定具体的类型。

其语法为:

//定义模板参数T可以用typename,也可以使用class
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(用泛型指定的参数列表)
{
}

以交换函数为例:

template<typename T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}
int main()
{
	int a = 10, b = 20;
	double c = 1.1, d = 2.2;
	Swap(a, b);
	Swap(c, d);
	cout << a << " " << b << endl;
	cout << c << " " << d << endl;
}

从反汇编可以看出,这两个函数调用的并不是同一个函数:

这是因为函数模板不是一个实际的函数,编译器不会为其生成可执行代码。当调用函数模板时,编译器会对函数模板进行推演,根据传入实参的类型推出T的类型,然后实例化出不同类型的函数。

1.1.1.两种函数模板的实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。

1.隐式实例化

让编译器根据实参推演模板参数的实际类型

当有多个类型的实参而模板参数列表中只有一个T时,编译器将无法推演出T的类型,此时可以将实参进行类型强转:

有趣的是,强转后需要用const T来接收,因为强转后传入的并不是c,而是c的临时变量,这个临时变量是具有常属性的。

2.显式实例化

在函数名后的<>中指定模板参数的实际类型

通过这种方式可以不让编译器推演类型,而是使用我们指定的类型。

当然对于类型不同的参数也要使用const T来接收。

1.1.2.模板参数的匹配原则

一个非模板函数可以和一个同名的函数模板同时存在,调用的时候如果与非模板函数匹配,编译器会优先调用非模板函数。如果非模板函数不匹配或者进行了实例化,则会调用函数模板。

1.2.类模板

对于一个类的成员变量也可以使用模板,这样在定义类对象的时候就可以实例化出具有不同类型的成员变量和成员函数的对象了。

如果类模板中函数放在类外进行定义时,需要加模板参数列表,否则会找不到T。

模板也不支持分离编译,建议定义在一个文件中。

以动态顺序表为例:

template<class T>
class Vector
{
public:
	Vector(size_t capacity = 10)
		: _pData(new T[capacity])
		, _size(0)
		, _capacity(capacity)
	{}
	~Vector();
	 //头插尾插等函数实现。。。
	size_t Size()
	{
		return _size;
	}
	T& operator[](size_t pos)
	{
		assert(pos < _size);
		return _pData[pos];
	}
private:
	T* _pData;
	size_t _size;
	size_t _capacity;
};
//类模板中函数放在类外进行定义时,需要加模板参数列表,否则会
template <class T>
Vector<T>::~Vector()
{
	if (_pData)
		delete[] _pData;
	_size = _capacity = 0;
}
int main()
{
	Vector<int> s1;
	Vector<double> s2;//实例化两个不同的类对象
	return 0;
}

类模板实例化与函数模板实例化不同,类模板实例化只能显示实例化,需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

二、STL

STL,英文全称 standard template library,中文可译为标准模板库或者泛型库,其包含有大量的模板类和模板函数,是 C++ 提供的一个基础模板的集合,用于完成诸如输入/输出、数学计算等功能。

STL有六大组件,但主要包含容器、算法和迭代器三个部分。

容器(Containers):用来管理某类对象的集合。各种数据结构,如vector、list、deque、set、map等,用来存放数据,从实现角度来看,STL容器是一种class template。
算法(Algorithms):用来处理对象集合中的元素,各种常用的算法,如sort、find、copy、for_each。从实现的角度来看,STL算法是一种function template。
迭代器(Iterators):用来在一个对象集合的元素上进行遍历动作。扮演了容器与算法之间的胶合剂,共有五种类型,从实现角度来看,迭代器是一种将operator* , operator-> , operator++, operator–等指针相关操作予以重载的class template。所有STL容器都附带有自己专属的迭代器,只有容器的设计者才知道如何遍历自己的元素。原生指针(native pointer)也是一种迭代器。
仿函数:行为类似函数,可作为算法的某种策略。从实现角度来看,仿函数是一种重载了operator()的class 或者class template。
适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
空间配置器:负责空间的配置与管理。从实现角度看,配置器是一个实现了动态空间配置、空间管理、空间释放的class tempalte。

STL存在以下缺陷:

STL库的更新太慢了。上一版靠谱是C++98,中间的C++03基本一些修订。C++11出来已经相隔了13年,STL才进一步更新。STL现在都没有支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取。STL的使用会有代码膨胀的问题,比如使用vector/vector/vector这样会生成多份代码,当然这是模板语法本身导致的。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C++ 标准模板类详解

    目录 1 标准模板库 2.泛型编程 总结 1 标准模板库 STL提供了表示容器.迭代器.函数对象和算法的模板. 容器:类似数组存储若干值,切实同质的: 迭代器:遍历容器的对象,类似遍历数组的指针,广义指针: 算法:完成特定的任务: 函数对象:类对象或函数指针. 模板类 vector erase() 删除矢量中给定区间元素.接受两个迭代器参数(该参数定义了要删除的区间),迭代器1指向区间起始处,迭代器2指向区间终止处的后一个位置. // delete first and second elemen

  • C++标准模板库STL的介绍

    介绍 STL(standard template library)标准模板库,是一种高效的C++程序库. STL分为三类:container(容器),iterator(迭代器)和algorithm(算法). 容器(container) 容器是用来管理某一类对象的集合,包括序列式容器和关联式容器. 序列式容器:vector,list,deque 关联式容器:map,set 遵循规则: 1.所有容器中存放的都是值而非引用.因此容器中的每个元素必须能够被拷贝,如想存放的不是副本,容器元素只能是指针.

  • C++ 标准模板库 STL 顺序容器详解

    C++ 标准模板库 STL 顺序容器 容器 数据结构 顺序性 重复性 支持迭代器 vector 动态数组 无序 可重复 随机访问迭代器 deque 双向队列 无序 可重复 随机访问迭代器 list 双向链表 无序 可重复 双向迭代器 动态数组 vector ​ vector #include <vector> 动态数组:其元素在内存中是连续存放的,随机存取任何元素都可以在常数时间内完成,在该容器的尾部增删元素也几乎能够在常数时间内完成具有较好的性能. ​ 一个 vector 常用函数使用实例如

  • 详解C++ STL vector容器访问元素的几种方式

    学会如何创建并初始化 vector 容器之后,本节继续来学习如何获取(甚至修改)容器中存储的元素. 访问vector容器中单个元素 首先,vector 容器可以向普通数组那样访问存储的元素,甚至对指定下标处的元素进行修改,比如: #include <iostream> #include <vector> using namespace std; int main() { vector<int> values{1,2,3,4,5}; //获取容器中首个元素 cout &l

  • C++ STL 四种智能指针的用法详解

    0.前言 C++ 标准模板库 STL(Standard Template Library) 一共给我们提供了四种智能指针:auto_ptr.unique_ptr.shared_ptr 和 weak_ptr,其中 auto_ptr 是 C++98 提出的,C++11 已将其摒弃,并提出了 unique_ptr 替代 auto_ptr.虽然 auto_ptr 已被摒弃,但在实际项目中仍可使用,但建议使用更加安全的 unique_ptr,后文会详细叙述.shared_ptr 和 weak_ptr 则是

  • 详解c++中的trait与policy模板技术

    目录 概述 trait模板技术 用模板参数来传递多种trait policy模板技术 模板化的policy trait模板与policy模板技术的比较 概述 我们知道,类有属性(即数据)和操作两个方面.同样模板也有自己的属性(特别是模板参数类型的一些具体特征,即trait)和算法策略(policy,即模板内部的操作逻辑).模板是对有共性的各种类型进行参数化后的一种通用代码,但不同的具体类型又可能会有一些差异,比如不同的类型可能会有自己的不同特征和算法实现策略. trait模板技术 当在模板代码中

  • C++中模板和STL介绍详解

    目录 一.模板 1.1.函数模板 1.1.1.两种函数模板的实例化 1.1.2.模板参数的匹配原则 1.2.类模板 二.STL 总结 一.模板 对于一个交换函数,虽然C++支持函数重载,我们可以对多个交换函数起相同的名字: void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } void Swap(double& left, double& right) { doub

  • c++中的单例类模板的实现方法详解

     1.什么是单例模式 在架构设计时,某些类在整个系统生命周期中最多只能有一个对象存在 ( Single Instance ).如超市收银系统,其外观主要由显示器(1个).扫描枪(1个).收款箱(1个)组成,在系统正常运行期间这三部分都是唯一存在的:也就是说,显示器.扫描枪.收款箱这三部分都应该有各自的类,并且每个类只能唯一地实例化一个对象,基于这种模式的程序设计,称为单例模式. !!!单例模式只能创建一个对象,且该对象的生命周期伴随系统的整个运行期间. 2.怎么实现单例模式 思考:如何定义一个类

  • C#中List用法介绍详解

    目录 一.#List泛型集合 为什么要用泛型集合? a.使用ArrayList b.使用自定义集合类 什么是泛型? 怎样创建泛型集合? 泛型集合的排序 泛型集合的搜索 泛型集合的扩展 二.List的方法和属性 方法或属性 作用 三.List的用法 1.List的基础.常用方法: (1).声明: (2).添加元素: (3).删除元素: (5).给List里面元素排序: (6).给List里面元素顺序反转: (7).List清空: (8).获得List中元素数目: 2.List的进阶.强大方法: (

  • 基于Django模板中的数字自增(详解)

    Django框架的模板提供了{% for %} 标签来进行循环 例如对集合进行循环是比较简单的 {% for row in v1 %} <div>{{row.name}}</div> {% endfor %} 但是在Django中,并不直接支持形如"int i = 0;i<100;i++" 这样的循环,Django有自己的自增方法 假设v1内有2个元素 1,从1开始正向自增 结果1,2 {% for row in v1 %} <div>{{fo

  • C++中 STL list详解及简单实例

    C++中 STL list详解 1.List: 内部实现是一个双向链表,可以高效的进行插入删除,但不能够进行随机访问 2..示例程序: #include "stdafx.h" #include <iostream> #include <list> #include <iterator> #include <algorithm> using namespace std; const int num[5] = {1,3,2,4,5}; boo

  • C/C++中栈(stack)&堆(heap)详解及其作用介绍

    目录 概述 程序运行中的栈和堆 堆和栈的差异 申请方式和回收方式 申请后系统的响应 申请效率比较 申请大小的限制 堆和栈中的存储内容 概述 栈 (stack) 是为执行线程流出的内存空间. 堆 (head) 是为动态分配预留的空间. 程序运行中的栈和堆 我们以一段代码来举例: #include <iostream> using namespace std; int a = 0; // 全局初始化区 char *pt; // 全局未初始化 int main() { int b; // b在栈区

  • C/C++中文件的随机读写详解及其作用介绍

    目录 概述 随机读写 函数 例子 指针流成员函数 随机访问二进制数据 概述 文件的操作方式分为顺序读写和随机读写. 顺序读写指文件的指针只能从头移到尾巴. 随机读写指文件指针可以随意移动, 根据需要. 随机读写 文件指针: 在磁盘文件操作中有一个文件指针, 用来指明进行读写的位置. 函数 文件流提供了一些有关文件指针的成员函数: 成员函数 作用 gcount() 返回最后一次输入所读入的字节数 tellg() 返回输入文件指针的当前位置 seekg (文件中的位置) 将输入文件中指针移到指定的位

  • C/C++中派生类访问属性详解及其作用介绍

    目录 保护继承 派生类成员的访问属性 总结 保护继承 由 protected 声明的成员称为 "受保护的成员", 或简称 "保护成员". 从用户的角度来看, 保护成员等价于私有成员. 保护成员可以被派生类的成员函数引用. 派生类成员的访问属性 4 种访问属性: 公用的: 类内和类外都可以访问 受保护的: 类内可以访问, 类外不能访问, 下一层的派生类可以访问 私有的: 类内可以访问, 类外不能访问 不可访问的: 类内和类外都不能访问 继承方式 基类中的成员 访问属性

  • C++中浅拷贝与深拷贝的详解及其作用介绍

    目录 概述 对象的赋值 对象的复制 对象复制的用途 建立一个新对象 函数的参数为类对象 函数的返回值为类对象 浅拷贝 深拷贝 概述 浅拷贝 (shallow copy) 只是对指针的拷贝, 拷贝够两个指针指向同一个内存空间. 深拷贝 (deep copy) 不但对指针进行拷贝, 而且对指针指向的内容进行拷贝. 经过深拷贝后的指针是指向两个不同地址的指针. 对象的赋值 同类对象之间可以相互赋值. 对象赋值的一般形式: 对象名1 = 对象名2; 举个栗子: int main() { Time t1(

  • C++中static修饰符的详解及其作用介绍

    目录 概述 静态数据成员 引用静态数据成员 用类名访问数据成员 静态成员函数 综合案例 概述 static (静态) 修饰符是用来控制变量的存储方式和可见性的. 静态局部变量存储在静态区域: static 的性质: 局部特性:作用范围仅限于本函数 静态特性:存储在静态区, 函数调用结束后不孝顺而保留原值. 在下一次调用时, 保留上一次调用结束时的值. 静态数据成员 在我们定义全局变量的时候, 我们会发现一个问题: 我们可以在程序各处自由的修改全局变量的值 (不安全). 静态数据成员的特点: 静态

随机推荐