c++模板初识,STL简介

普通young man 2024-07-29 12:35:03 阅读 77

前言

本篇博客讲解一下c++的类和对象,看这个之前请先看:C++内存管理-CSDN博客

💓 个人主页:普通young man-CSDN博客

⏩ 文章专栏:C++_普通young man的博客-CSDN博客

⏩ 本人giee:普通小青年 (pu-tong-young-man) - Gitee.com

      若有问题 评论区见📝

🎉欢迎大家点赞👍收藏⭐文章

        

目录

泛型编程

函数模板

函数的概念

函数模板格式

函数模板的原理

函数模板的实例化

隐式实例化

多参数模板

显示实例化

模板参数的匹配原则

类模板

类模板的定义格式

​编辑

类模板的实例化


简单的说一下模板的概念,你们应该听过活字印刷吧,c++的模板和这个概念差不多,就是说,你一种函数或者类,你给他一个模板,他就可以传任何类型的变量,这个也叫泛型编程。


泛型编程

这里实现一下能传不同类型参数的Swap(交换函数)

<code>void Swap(int& x,int& y) {

int tmp = x;

x = y;

y = tmp;

}

void Swap(double& x, double& y) {

double tmp = x;

x = y;

y = tmp;

}

void Swap(char& x, char& y) {

char tmp = x;

x = y;

y = tmp;

}

int main() {

int p1 = 1, p2 = 2;

double a1 = 1.0, a2 = 2.0;

char b1 = 'x', b2 = 'y';

Swap(p1, p2);

Swap(a1, a2);

Swap(b1, b2);

cout << p1 << ' ' << p2 << endl;

cout << a1 << ' ' << a2 << endl;

cout << b1 << ' ' << b2 << endl;

}

这里其实是函数重载实现的这些,但是函数重载是有缺点的:

第一:每需要一个新的类型,就要新写一个函数。

第二:如果一个函数报错,其他函数也用不了。

那有什么方法可以帮助我们解决这个问题?

那现在我们就可以给一个摸具,你想他是什么类型,他就是什么类型,比如:

        如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同 材料的铸件(即生成具体类型的代码),那将会节省许多头发。巧的是前人早已将树栽好,我们只 需在此乘凉。

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。


函数模板

函数的概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生 函数的特定类型版本。

函数模板格式

<code>template<class T,class T...>

返回类型 函数名(参数列表) {

}

把刚才的Swap用模板实现一下

template <class T>

void Swap(T& x,T& y) {

T tmp = x;

x = y;

y = tmp;

}

int main() {

int x = 10,y = 20;

double a = 10.2, b = 20.1;

Swap(a, b);

Swap(x,y);

cout << x << ' ' << y << endl;

cout << a <<' '<< b << endl;

}

你会发现他什么类型都可以传,这边我只传了两种类型

注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替 class)

函数模板的原理

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。 所以其实模板就是将本来应该我们做的重复的事情交给了编译器

编译器需要根据传入的数据来推演出对应的函数类型,编译器需要根据传入的实参类型来推演生成对应 类型的函数。

这边我举一个错误的例子:

这边我直接传入数字,你会发现报错了,这是为什么,其实这是因为编译器他是通过传的实际参数来推演你的类型,但是你的实参就是一个数字,编译器也不知道你要传的是什么类型,所以他报了个错误

函数模板的实例化

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

隐式实例化

隐式实例化就是让编译器自己推演出需要的类型

<code>template<class T>

T Add(const T& x,const T& y) {

return x + y;

}

int main() {

int a = 10, b = 20;

double c = 10.1, d = 20.2;

cout << Add(a, b) << endl;;

cout << Add(c, d) << endl;;

}

当a,b,c,d被传到模板时候他就会推演出需要的类型,来满足要求

假如你这样写

这边我第一个参数是int第二个参数是double,编译器就无法推演出你到底是用int还是double,这里有两种方法,第一种是显示实例化,第二种就是增加模板参数(下面我会讲)

多参数模板

多参数模板就是可以多个实参类型传参数,他是根据你第一个参数来决定你后面参数的类型

<code>template<class T1,class T2>

T1 Add(const T1& x,const T2& y) {

return x + y;

}

int main() {

int a = 10, b = 20;

double c = 10.1, d = 20.5;

/*cout << Add<int>(a, d) << endl;

cout << Add<double>(c, b) << endl;*/

cout << Add(b,d) << endl;

cout << Add(c, b) << endl;

}

显示实例化

显示实例化就是直接给编译器说,这个类型是什么,然后你传参数是传的不是一个类型,这里就会走隐式类型转换

这里我用<类型>来告诉编译器这里是int类型,即便我传的是double类型,他会通过一个隐式类型转换把他转换成int类型,这个就是显示实例化。

模板参数的匹配原则

1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这 个非模板函数

2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而 不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

如果一个你想计算Add,你调用了这个函数,他会去调用模板函数还是非模板函数,这里调用的就是非模板函数,可以看一下汇编

可以发现模板函数和非模板函数的地址是不一样的,其实也可以发现编译器很聪明,知道有现成的直接用的道理

3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

这个就是说函数模板需要<int>或者多参数才能走这个隐式类型转换,但是函数不需要,它可以直接转换


类模板

类模板的定义格式

<code>template<class T1, class T2, ..., class Tn>

class 类模板名

{

// 类内成员定义

};  

这边我直接用类模板写一个栈,你就知道他有多爽多方方便了

template <class T>

class Stack

{

public:

Stack(int capacity = 4){

_arr = new T[capacity];

_size = 0;

_capacity = capacity;

}

void Push(const T& data) {

if (_size == _capacity)

{

int capacity_s = _capacity * 2;

T* tmp = new T[capacity_s];

memcpy(tmp, _arr, sizeof(capacity_s));

_arr = tmp;

_capacity = capacity_s;

}

_arr[_size++] = data;

}

private:

T* _arr;

int _size;

int _capacity;

};

int main() {

//实例化模板

Stack<int> s1;//int

Stack<double> s2;//double

s1.Push(1);

s1.Push(2);

s2.Push(2.15);

s2.Push(2.16);

}

如果是以前C语言是不是得写几个函数才能传不同类型,这里自己就可以实例化不同类型得类。

类模板的实例化

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

好了今天得博客就到这里了,谢谢大家得观看

STL简介

STL(Standard Template Library,标准模板库)是C++标准库的一个重要组成部分,它提供了一系列可重用的组件,包括容器、算法、迭代器和函数对象,用于高效地处理数据结构和算法问题。STL的设计原则是将数据和操作数据的算法分离,这使得它可以非常灵活地应用于各种不同的场景。

容器(Container)

容器是STL中用于存储数据的对象。它们可以分为以下几类:

序列容器:如 <code>vector、list 和 deque,这些容器按顺序存储元素,支持快速随机访问(对于 vector 和 deque),或高效的插入和删除操作(对于 list)。关联容器:如 setmapmultiset 和 multimap,这些容器基于红黑树实现,元素根据键值排序,并且支持对数时间复杂度的查找、插入和删除操作。容器适配器:如 stackqueue 和 priority_queue,它们是基于其他容器构建的,提供特定的接口,如后进先出(LIFO)或先进先出(FIFO)的行为。

算法(Algorithm)

STL算法是一系列通用函数,可以应用于任何符合一定要求的序列上。这些算法包括:

修改序列:如 sortreverse 和 fill查询序列:如 findcount 和 equal_range变换序列:如 transform 和 copy归并序列:如 merge 和 inplace_merge数值算法:如 accumulate 和 inner_product

迭代器(Iterator)

迭代器是一种可以遍历容器元素的对象,类似于指针,但功能更强大,能够处理不同类型容器的内部细节。迭代器提供了访问容器元素的一致接口,使得算法可以独立于容器的类型工作。

函数对象(Function Object)

函数对象,也被称为仿函数,是一种行为类似函数的类,通过重载括号运算符 operator() 来实现。它们可以被用作算法的参数,用于自定义比较、操作等逻辑。

STL的设计强调了泛型编程,这意味着它的组件可以在不知道具体数据类型的情况下工作,从而提高了代码的重用性和灵活性。此外,STL还提供了丰富的异常安全性和性能优化特性,使其成为现代C++编程不可或缺的一部分。

总的来说,STL提供了一套经过优化、高度可读且易于使用的工具,极大地简化了C++程序的开发过程,减少了常见的编程错误,同时保持了高性能。

STL学习的阶段

简单总结一下:学习STL的三个境界:能用,明理,能扩展



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。