C++初阶模板

news/2025/2/11 19:44:11/

目录

    • 函数模板
      • 函数模板的实例化
    • 类模板
      • 类模板的实例化
      • 类模板声明和定义分离的情况

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;
}

要实现一个通用的交换函数,函数重载虽然可以实现,但是有一下几个不好的地方:

  1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
  2. 代码的可维护性比较低,一个出错可能所有的重载均出错

函数模板

template<typename T1, typename T2,…,typename Tn>
返回值类型 函数名(参数列表){}
注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)

// 模板
// Type
//template<class T>
//template<typename T>
//template<typename T1, typename T2>
//
// 编译器用模板实例化生成对应的Swap函数
//
// 函数模板
template<class T>
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}int main()
{int i = 0, j = 1;Swap(i, j);double x = 1.1, y = 2.2;Swap(x, y);return 0;
}

执行的指令不一样,调用函数要建立栈帧 大小不同建立的栈帧不一样,int开4个字节double开了8字节,所以调用的不是同一个函数

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此

编译器根据模板会实例化生成对应函数
不是所有都生成,用到什么就生成什么

函数模板的实例化

  1. 隐式实例化:让编译器根据实参推演模板参数的实际类型
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{cout << Add(1, 2) << endl;cout << Add(1.1, 2.2) << endl;return 0;
}
  1. 显式实例化:在函数名后的<>中指定模板参数的实际类型
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{cout << Add(1, 2) << endl;//cout << Add(1.1, 2) << endl;//如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。注意:在模板中,编译器一般不会进行类型转换操作
此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化cout << Add((int)1.1, 2) << endl;cout << Add(1.1, (double)2) << endl;// 显示实例化cout << Add<int>(1.1, 2) << endl;cout << Add<double>(1.1, 2) << endl;return 0;
}

如果有自己写的函数,优先调用自己写的,如果要指定模板的就要显示实例化

int Add(int left, int right)
{return left + right;
}// 通用加法函数
template<class T>
T Add(T left, T right)
{return left + right;
}cout << Add(x, y) << endl;cout << Add<int>(x, y) << endl;//指定调用模板函数

无法推演类型时必须要显示实例化:

template<class T>
T* func(int n)
{return new T[n];
}
int* ptr = func<int>(10);

类模板

格式

template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};

类模板的实例化

typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 4){cout << "Stack()" << endl;_array = new DataType[capacity];//_array = new char[0x7fffffff];_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){cout << "~Stack()" << endl;delete[] _array;_array = nullptr;_size = _capacity = 0;}
private:// 内置类型DataType* _array;int _capacity;int _size;
};
int main()
{Stack st1; // intStack st2; // doublereturn 0;
}

一个栈存int,一个栈存double时typedef就不能很好解决,这时候可以用模板解决

template<class T>
class Stack
{
public:Stack(size_t capacity = 4){cout << "Stack()" << endl;_array = new T[capacity];_capacity = capacity;_size = 0;}void Push(const T& data)//T有可能是个自定义类型,加上引用{// CheckCapacity();_array[_size] = data;_size++;}~Stack(){cout << "~Stack()" << endl;delete[] _array;_array = nullptr;_size = _capacity = 0;}
private:T* _array;int _capacity;int _size;
};int main()
{// Stack类名,Stack<int>才是类型Stack<int> st1; // intStack<double> st2; // doublereturn 0;
}

函数模板可以通过对实参类型的推演,确定T的类型
类模板就需要显式实例化了
类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

类模板声明和定义分离的情况

对于普通的类,类名就是类型,所以使用时不用加上struct
类模板的类名不是类型

template<class T>
class Vector
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}// 析构函数演示:在类中声明,在类外定义。~Vector();void PushBack(const T& data);void PopBack();// ...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;
};//只能在类里用T,所以声明和定义分离时还要单独声明一下T
template<class T>
Vector<T>::~Vector()
{delete[] _pData;_pData = nullptr;
}// 类名 : Vector
// 类型 : Vector<T>int main()
{Vector<int> v;return 0;
}

类模板中,类名不是类型
指定类域用类型,因为在类外面,T不知道在哪,所以声明和定义分离时还得单独声明一下模板

类模板时不要轻易的做声明和定义分离
在一个文件可以分离,在两个文件(.h和.cpp)分离会报错


http://www.ppmy.cn/news/1084118.html

相关文章

【Tkinter系列13/15】标准化外观和选项数据库

27. 标准化外观和选项数据库 可以轻松地将颜色、字体和其他选项应用于 小部件&#xff0c;当您创建它们时。然而 如果您希望很多小部件具有相同的 背景颜色或字体&#xff0c;指定每个都很乏味 每次选项&#xff0c;以及 让用户覆盖您的选择是很好的 他们最喜欢的配色方案、字…

【云原生进阶之PaaS中间件】第一章Redis-1.3Redis配置

1 Redis配置概述 Redis支持采用其内置默认配置的方式来进行启动&#xff0c;而不需要提前配置任何文件&#xff0c;但是这种启动方式只推荐在测试和开发环境中使用&#xff0c;但更好的方式是通过提供一个Redis的配置文件来对Redis进行配置&#xff0c; 这个配置文件一般命名为…

算法竞赛入门经典习题2-5 分数化小数 (decimal)

题目如下&#xff1a; 《算法竞赛入门经典》——习题2-5 分数化小数&#xff08;decimal&#xff09;_Simone Zeng的博客-CSDN博客 代码如下&#xff1a; #include <cstdio>int a, b, c, k;int main(){k 0;while(scanf("%d %d %d", &a, &b, &c…

【1day】H5S视频平台未授权漏洞学习

目录 一、漏洞描述 二、资产测绘 三、漏洞复现 四、漏洞修复 一、漏洞描述 H5S视频平台是一个基于Web技术的视频播放和流媒体管理平台。它提供了一套完整的解决方案,用于在网页上播放和管理视频内容。H5S视频平台存在未授权漏洞,泄露内网rtsp服务集群的服务集群的和H5_…

Python 爬虫—scrapy

scrapy用于从网站中提取所需数据的开源协作框架。以一种快速、简单但可扩展的方式。 该爬虫框架适合于那种静态页面&#xff0c; js 加载的话&#xff0c;如果你无法模拟它的 API 请求&#xff0c;可能就需要使用 selenium 这种使用无头浏览器的方式来完成你的需求了 入门 imp…

Leetcode1090. 受标签影响的最大值

思路&#xff1a;根据值从大到小排序&#xff0c;然后在加的时候判断是否达到标签上限即可&#xff0c;一开始想用字典做&#xff0c;但是题目说是集合却连续出现两个8&#xff0c;因此使用元组SortedList进行解决 class Solution:def largestValsFromLabels(self, values: li…

【UIPickerView案例03-点餐系统之随机点餐 Objective-C语言】

一、先来看看我们这个示例程序里面,随机点餐是怎么做的 1.点击:“随机点餐”按钮 大家能想到,它是怎么实现的吗 1)首先,点击”随机点餐“按钮,的时候,你要让这个pickerView,进行随机选中,那么,得监听它的点击 2)然后呢,让pickeView选中数据, 3)然后呢,把那个…

MySQL中的索引事务(1)索引----》数据库运行的原理知识+面试题~

本篇文章主要讲述MySQL索引事务 所谓的索引index就是指&#xff1a;目录~~ 索引存在的意义&#xff1a;加快查找的速度&#xff08;省略了遍历的过程&#xff09;&#xff0c;但付出了一定的代价~ 付出的代价如下&#xff1a; 1.需要付出额外的空间代价来保存索引数据2.索引…