2.1通过模板初始STL思维
1.它提供了一种通用的方法来开发可重用的代码,模板本质上就是参数化多态,一种使用无类型参数产生一系列函数或类的机制。即可以创建参数化的 C++ 类型。
模板分为两种类型函数模板和类模板 函数模板是用于生成函数的,类模板则是用于生成类的。
函数模板:创建一个通用功能函数支持多种不同的形参,进一步简化重载函数。
函数模板的基本写法为:
template <typename 类型参数1, typename 类型参数2, ...>
返回值类型 函数模板名(形参表)
{
函数体
}
PS: typename 关键字也可以用 class 关键字替换
函数模板有如下两种调用方法:
第一种:函数名 <实际类型> (参数);
显式类型推导 fun <int> (1, 2);
第二种:函数名(参数);
隐形类型推导 fun (2.2, 1.5);
#include <iostream>
using namespace std;
template <typename T>
void add_fun(T a, T b) //函数模板
{
cout << "a + b = " << a + b << endl;
}
int main(int argc, char const *argv[])
{
add_fun<int>(10, 2); //显示类型推导
add_fun(12.1, 9.8); //隐式类型推导
return 0;
}
类函数:允许用户为类定义一种模式,使类中某些数据成员,某些成员函数的参数,某些成员函数返回值可取任意类型(不是具体的类,是一类的类,相当于类的集合。
类模板的写法如下:
template <class 类形参数1, class 类型参数2, ...>
class 类模板名称
{
成员函数和成员变量
};
实例化(调用)格式:类名 <类型> 对象名(参数);
#include <iostream>
using namespace std;
template <typename T>
class person
{
public:
T age;
};
int main(int argc, char const *argv[])
{
person<int> people; //通过无参构造的方式构建一个名叫people的对象
return 0; //即使没有传入参数,也必须指定模板中T的具体类型
}
2.函数模板的用法同C++ 预处理器的用法有一定的类似之处,它们都提供编译代码过程中的文本替换功能,但前者可以对类型进行一定的保护。
3.使用类模板可以编写通用的、类型安全的类。
例1:
本示例是一个简单的动态数组板类,能体现出 STL 容器关于内存“动态分配、销毁再分配”的思想。
#include<stdio.h>
template<class T>
class MyArray//定义类
{
private:
int m_nTotalSize;//数组总长度 私有整型成员变量
int m_nValidSize;//数组有效长度
T*m_pDate;//表示存储数据的数组
public:
MyArray(int nSize=3)//数组默认总长度是 3
{
m_pData=new T[nSize];// 使用 new 运算符动态分配大小为 nSize 的数组,并将指针赋值给m_pData
m_nTotalSize=nSize;//将成员变量 m_nTotalSize的值设置为nSize
m_nValidSize=0;//初始值为0
}
void Add(T value)//定义一个共有的add,向m_pData添加数据,参数为要添加的数据value
{
if(m_nValidSize<m_nTotalSize)//如果有效长度小于总长度
{
m_pData[m_nValidsize]=value;//则赋值 将value的值赋给数组中下一个位置
m_nValidsize++;//有效长度加1
}
else
{
int i=0;
//以下为重点 !!!!!!!!!!!!!通过代码,调用方可随意添加元素,实现了动态数组的生成。
T * tmpData=new T[m_nTota1sise];//原始数据备份
//使用 new 运算符动态分配一个大小为 m_nTotalSize的临时数组 tmpData。
for(i=0; i<m_nTotalgize; i++)//循环遍历原始数据,将数据复制到临时数组
{
tmpData[i]=m_pData[i];
}
delete []m_pData;//释放原始数据内存空间
m_nTotalsize * =2;//原始数据空间重新分配,空间扩大2倍
m_pData=new T[m_nTotalsize];//传回备份数据
for(i=0;i<m_nValidsize; i++)//循环遍历临时数组,将数据复制回原始数组。
{
m_pData[i]=tmpData[i] ;
}
delete []tmpData;
m_pData[m_nValidSize]=value;//将value的值赋给数组中的下一个位置
m_nValidsize++ ;
}
}
int GetSize()//定义公有方法返回数组有效 长度
{
return m_nValidSize;//返回成员变量的值
}
T Get(int pos)//定义公有方法get,返回某一位置元素 ,参数为位置pos
{
return m_pData[pos];//返回数组中位置在pos的元素
}
virtual~MyArray()//定义一个公有的析构函数
{
if(m_pData!=NULL)//指针m_pDate的值不为真
{
delete []m_pData;//释放数组的内存空间
m_pData=NULL;
}
}
};
int main(int argc, char * argv[])//定义主函数
{
MyArray<int>obj;//声明一个 MyArray 类型的对象 obj,模板参数为 int。
obj.Add(1);//向obj数组中添加元素1和2和3和4
obj.Add(2);
obj.Add(3);
obj.Add(4);
for(int i=0;i<obj.GetSize();i++) //循环遍历数组中的元素
{
printf("%d\n",obj.Get(i));
}
return 0;
}
例2:编制一个数组元素求和的函数模板:
非模板函数(整型数组元素和)
int sum(int data[ ],int nSize)
{
int sum=0;
for(int i=0;i<nsize;i++)
sum+=data[il;
return sum;
}
模板函数(任意类型数组元素和)
template<class T>
T sum( T data[ ],int nSize)
{
T sum=0;
for(int i=0;i<nSize;i++)
sum+=data[i];
return sum;
}
2.2 习题
1、STL标准模板库编程的基本步骤 ①形成容器元素②取出所需要的迭代指针③调用通用算法
2. 问题1:模板实例化**
template <typename T>
void PrintValue(T value) {
std::cout << value << std::endl;
}
int main() {
PrintValue(5);
PrintValue(3.14);
PrintValue("Hello");
return 0;
}
上面的代码中,`PrintValue`是一个模板函数,用于打印传入的值。请指出在`main`函数中,`PrintValue`函数被实例化了多少次,并解释为什么。
在main函数中,PrintValue函数被实例化了三次。一次是传入整数类型int的实例化,一次是传入浮点数类型double的实例化,最后一次是传入字符串类型const char*的实例化。每次调用PrintValue时,编译器会根据传入的参数类型生成相应的实例化代码
3. 问题2:模板特化**
template <typename T>
struct Adder {
T Add(T a, T b) {
return a + b;
} };
template <>
struct Adder<std::string> {
std::string Add(std::string a, std::string b) {
return a + " " + b;
}
};
int main() {
Adder<int> intAdder;
Adder<std::string> stringAdder;
int sum = intAdder.Add(2, 3);
std::string result = stringAdder.Add("Hello", "world");
return 0;
}
上面的代码中,`Adder`是一个模板结构体,用于执行加法运算。`Adder`有一个通用的模板实现和一个特化的实现,用于处理`std::string`类型的相加。请解释在`main`函数中,为什么`Adder<int>`和`Adder<std::string>`使用了不同的实现。
在main函数中,Adder<int>和Adder<std::string>使用了不同的实现是因为针对std::string类型的特化实现了Add函数。模板特化允许我们为特定的类型提供定制的实现,以满足特殊的需求。当我们使用Adder<int>时,编译器会选择通用的模板实现;而当我们使用Adder<std::string>时,编译器会选择特化的实现
4.编制动态数组模板类(调空间)
Template<class T>
{ int i=0;
T*tmpData=T[m_nTotalSize];
For(i=0;i< m_nTotalSize;i++)
{ tmpData [i]=m_pData[i]; }
delete []m_pData;
m_pData=new T[]m_nTotalSize;
for(i=0;i< m_nValidSize;i++){
m_pData[i]= tmpData [i]
}
delete []tmpData;
m_pData[m_nValidSize]=value;
m_nValidSize++;
}