c++的学习之路:19、模板

简介: c++的学习之路:19、模板

一、非类型模板参数

模板参数分类类型形参与非类型形参。

类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。

非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

类型模板在之前写代码的时候经常使用,就是template在之前代码中经常使用这个,这个就是模板可以让编译器自动推演类型,从而方便使用,如下方代码如果想要定义一个数组指定他的大小,在以前我是用宏定义的,但是如果需要修改的话还是挺麻烦的,还是需要一个一个去更改也会很麻烦这时,c++提出了一个该念叫做非类型模板。


#define n 10
namespace ly
{
    template
    class arry
    {
    private:
        int    arr[n];
    };
}


非类型模板 就是如下方代码这种给一个缺省值就可以利用这个缺省值去进行初始化数组的就是非类型模板,但是需要注意一下两点:

1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。

2. 非类型的模板参数必须在编译期就能确认结果。

template
    class arry
    {
    private:
        int    arr[n];
    };

二、类模板的特化

1、概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板,这里就不得不说说之前所用到的仿函数,在优先队列那里实现的代码就用了仿函数,但是有些情况就需要提出说一下,如下方代码和下方图片我想要的结果是小于,结果他却比较了指针大小这就不是我想要的,这时就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。


template
    bool Less(T left, T right)
    {
        return left < right;
    }    
cout << ly::Less(1, 2) << endl;
    Date d1(2022, 7, 7);
    Date d2(2022, 7, 8);
    cout << ly::Less(d1, d2) << endl;
    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << ly::Less(p1, p2) << endl;

2、函数模板特化

函数模板的特化步骤:

1. 必须要先有一个基础的函数模板

2. 关键字template后面接一对空的尖括号<>

3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型

4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误

下面的代码就是特化的写法,这个写法是祖师爷规定的,他的形式就是这种的。


template<>
    bool Less(Date* left, Date* right)
    {
        return *left < *right;
    }

3、类模板特化

类模板特化又分为全特化和偏特化,偏特化就是部分特化,全特化就是全部特化,如下方测试的代码所示。

下方这个代码就是全特化,全特化就是把参数全部都进行实例化,如下方带就是实例化成了int和char这样编译器就更加适配这个就不会在进行自动推演生成了。


namespace ly
{
    template
    class Data
    {
    private:
        T1 _d1;
        T2 _d2;
    };
    template<>
    class Data
    {
    private:
        int _d1;
        char _d2;
    };
}
int main()
{
    ly::Data d1;
    ly::Data d2;
}


偏特化也就是部分特化,这种只进行部分的实例化,就是偏特化的使用方式。


template
    class Data
    {
    private:
        int _d1;
        char _d2;
    };

三、模板的分离编译

1、什么是分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

也就是之前经常用的头文件与c文件分离使用,这个就是一个分离编译使用,相当于c语言中说的低耦合,也就是模块化。

2、模板的分离编译

但是在写c++的时候除了写日期类的时候用的还是分离,在后面写模板的时候就没有分离了,因为在我当时试了,报错搞了半天也没解决,我上网去查就查出来了一种解决方发,模板定义的位置显式实例化,但是特别麻烦,所以我就选择放在一个文件夹里面,然后我去看了一下stl的源码,代码如下,如图一就是放在类里面定义的,它定义的也就是少的就是放在类里,相当于内联长的也是放在外面写,如下方图二代码所示。

 

四、模板总结

1、优点

1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生

2. 增强了代码的灵活性

2、缺点

1. 模板会导致代码膨胀问题,也会导致编译时间变长

2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

五、代码

1、test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
#include <vector>
 
using namespace std;
 
//namespace ly
//{
//  template<class T,size_t n=10>
//  class arry
//  {
//  private:
//    int arr[n];
//  };
//  template<class T>
//  bool Less(T left, T right)
//  {
//    return left < right;
//  }
//  template<>
//  bool Less<Date*>(Date* left, Date* right)
//  {
//    return *left < *right;
//  }
//}
//
//int main()
//{
//  cout << ly::Less(1, 2) << endl;
//  Date d1(2022, 7, 7);
//  Date d2(2022, 7, 8);
//  cout << ly::Less(d1, d2) << endl;
//  Date* p1 = &d1;
//  Date* p2 = &d2;
//  cout << ly::Less(p1, p2) << endl;
//}
 
namespace ly
{
  template<class T1, class T2>
  class Data
  {
 
  private:
    T1 _d1;
    T2 _d2;
  };
  /*template<>
  class Data<int, char>
  {
 
  private:
    int _d1;
    char _d2;
  }*/
  template<class T1>
  class Data<T1, char>
  {
 
  private:
    int _d1;
    char _d2;
  };
}
 
int main()
{
  ly::Data<int, int> d1;
  ly::Data<int, char> d2;
}

2、Date.h

#pragma once
 
class Date
{
public:
  // 获取某年某月的天数
  int GetMonthDay(int year, int month);
  // 全缺省的构造函数
  Date(int year = 1, int month = 1, int day = 1);
  // 拷贝构造函数
  Date(const Date& d);
  // 赋值运算符重载
  Date& operator=(const Date& d);
  // 析构函数
  ~Date();
  // 日期+=天数
  Date& operator+=(int day);
  // 日期+天数
  Date operator+(int day);
  // 日期-天数
  Date operator-(int day);
  // 日期-=天数
  Date& operator-=(int day);
  // 前置++
  Date & operator++();
  // 后置++
  Date operator++(int);
  // 后置--
  Date operator--(int);
  // 前置--
  Date& operator--();
  // >运算符重载
  bool operator>(const Date& d);
  // ==运算符重载
  bool operator==(const Date& d);
  // >=运算符重载
  bool operator >= (const Date& d);
 
  // <运算符重载
  bool operator < (const Date& d);
  // <=运算符重载
  bool operator <= (const Date& d);
  // !=运算符重载
  bool operator != (const Date& d);
  // 日期-日期 返回天数
  int operator-(const Date& d);
  //打印
  void Print();
private:
  int _year;
  int _month;
  int _day;
};
 
 

3、Date.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "Date.h"
#include <iostream>
#include <string>
#include <vector>
 
using namespace std;
 
// 全缺省的构造函数
Date::Date(int year, int month, int day)
{
  _year = year;
  _month = month;
  _day = day;
}
// 拷贝构造函数
Date::Date(const Date& d)
{
  _year = d._year;
  _month = d._month;
  _day = d._day;
}
// 析构函数
Date::~Date()
{
  _year = 0;
  _month = 0;
  _day = 0;
}
// 赋值运算符重载
Date& Date::operator=(const Date& d)
{
  if (this!=&d)
  {
    _year = d._year;
    _month = d._month;
    _day = d._day;
  }
  return *this;
}
// 日期+=天数
Date& Date::operator+=(int day)
{
  if (day < 0)
  {
    return *this -= (-day);
  }
  _day += day;
  while (_day > GetMonthDay(_year, _month))
  {
    _day -= GetMonthDay(_year, _month);
    ++_month;
    if (_month == 13)
    {
      ++_year;
      _month = 1;
    }
  }
  return *this;
}
// 日期+天数
Date Date::operator+(int day)
{
  Date tmp(*this);
  tmp += day;
  return tmp;
}
// 日期-天数
Date Date::operator-(int day)
{
  Date tmp(*this);
  tmp -= day;
  return tmp;
}
// 日期-=天数
Date& Date::operator-=(int day)
{
  if (day < 0)
  {
    return *this += (-day);
  }
  _day -= day;
  while (_day <= 0)
  {
    --_month;
    if (_month == 0)
    {
      --_year;
      _month = 12;
    }
 
    _day += GetMonthDay(_year, _month);
  }
  return *this;
}
// 前置++
Date& Date::operator++()
{
  *this += 1;
  return *this;
}
// 后置++
Date Date::operator++(int)
{
  Date tmp(*this);
  *this += 1;
  return tmp;
}
// 后置--
Date Date::operator--(int)
{
  Date tmp(*this);
  *this -= 1;
  return tmp;
}
// 前置--
Date& Date::operator--()
{
  *this -= 1;
  return *this;
}
// <运算符重载
bool Date::operator < (const Date& d)
{
  if (_year < d._year)
  {
    return true;
  }
  else if (_year == d._year && _month < d._month)
  {
    return true;
  }
  else if (_year == d._year && _month == d._month && _day < d._day)
  {
    return true;
  }
  else
  {
    return false;
  }
}
// ==运算符重载
bool Date::operator==(const Date& d)
{
  return _year == d._year
    && _month == d._month
    && _day == d._day;
}
// <=运算符重载
bool Date::operator <= (const Date& d)
{
  return *this < d || *this == d;
}
// !=运算符重载
bool Date::operator != (const Date& d)
{
  return !(*this == d);
}
// >运算符重载
bool Date::operator>(const Date& d)
{
  return !(*this <= d);
}
// >=运算符重载
bool Date::operator >= (const Date& d)
{
  return !(*this < d);
}
// 日期-日期 返回天数
int Date::operator-(const Date& d)
{
  Date max = *this;
  Date min = d;
  int flag = 1;
  if (*this < d)
  {
    max = d;
    min = *this;
    flag = -1;
  }
  int n = 0;
  while (min != max)
  {
    ++min;
    ++n;
  }
  return n * flag;
}
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
  static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
  31 };
  int day = days[month];
  if (month == 2
    && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
  {
    day += 1;
  }
  return day;
}
//打印
void Date::Print()
{
  cout << "Print:" << _year << '/' << _month << '/' << _day << endl << endl;
}


六、导图


目录
相关文章
|
2天前
|
编译器 C++
【C++】继续学习 string类 吧
首先不得不说的是由于历史原因,string的接口多达130多个,简直冗杂… 所以学习过程中,我们只需要选取常用的,好用的来进行使用即可(有种垃圾堆里翻美食的感觉)
8 1
|
2天前
|
算法 安全 程序员
【C++】STL学习之旅——初识STL,认识string类
现在我正式开始学习STL,这让我期待好久了,一想到不用手撕链表,手搓堆栈,心里非常爽
16 0
|
2天前
|
存储 安全 测试技术
【C++】string学习 — 手搓string类项目
C++ 的 string 类是 C++ 标准库中提供的一个用于处理字符串的类。它在 C++ 的历史中扮演了重要的角色,为字符串处理提供了更加方便、高效的方法。
18 0
【C++】string学习 — 手搓string类项目
|
2天前
|
存储 算法 编译器
C++的模板与泛型编程探秘
C++的模板与泛型编程探秘
11 0
|
2天前
|
编译器 C++
【C++从练气到飞升】08---模板
【C++从练气到飞升】08---模板
|
2天前
|
算法 编译器 C++
【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]
【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]
23 1
|
2天前
|
编译器 C语言 C++
【C++】模板进阶
【C++】模板进阶
14 1
|
2天前
|
存储 编译器 C++
【C++】内存管理和模板基础(new、delete、类及函数模板)
【C++】内存管理和模板基础(new、delete、类及函数模板)
24 1
|
2天前
|
C++
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
|
2天前
|
存储 算法 C++
详解C++中的STL(标准模板库)容器
【4月更文挑战第30天】C++ STL容器包括序列容器(如`vector`、`list`、`deque`、`forward_list`、`array`和`string`)、关联容器(如`set`、`multiset`、`map`和`multimap`)和容器适配器(如`stack`、`queue`和`priority_queue`)。它们为动态数组、链表、栈、队列、集合和映射等数据结构提供了高效实现。选择合适的容器类型可优化性能,满足不同编程需求。