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
)。关联容器:如set
、map
、multiset
和multimap
,这些容器基于红黑树实现,元素根据键值排序,并且支持对数时间复杂度的查找、插入和删除操作。容器适配器:如stack
、queue
和priority_queue
,它们是基于其他容器构建的,提供特定的接口,如后进先出(LIFO)或先进先出(FIFO)的行为。
算法(Algorithm)
STL算法是一系列通用函数,可以应用于任何符合一定要求的序列上。这些算法包括:
修改序列:如
sort
、reverse
和fill
。查询序列:如find
、count
和equal_range
。变换序列:如transform
和copy
。归并序列:如merge
和inplace_merge
。数值算法:如accumulate
和inner_product
。
迭代器(Iterator)
迭代器是一种可以遍历容器元素的对象,类似于指针,但功能更强大,能够处理不同类型容器的内部细节。迭代器提供了访问容器元素的一致接口,使得算法可以独立于容器的类型工作。
函数对象(Function Object)
函数对象,也被称为仿函数,是一种行为类似函数的类,通过重载括号运算符
operator()
来实现。它们可以被用作算法的参数,用于自定义比较、操作等逻辑。
STL的设计强调了泛型编程,这意味着它的组件可以在不知道具体数据类型的情况下工作,从而提高了代码的重用性和灵活性。此外,STL还提供了丰富的异常安全性和性能优化特性,使其成为现代C++编程不可或缺的一部分。
总的来说,STL提供了一套经过优化、高度可读且易于使用的工具,极大地简化了C++程序的开发过程,减少了常见的编程错误,同时保持了高性能。
STL学习的阶段
简单总结一下:学习STL的三个境界:能用,明理,能扩展 。
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。