第二章 模板--2.1

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

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值