必看!用示例代码学C++模板编程,快速掌握基础知识,高效提升编程能力_函数模板

C++模板编程


目录
C++ 模板编程
●1.泛型编程
●2.函数模板
●3.类模板
●4.非类型模板参数
●5.模板的特化
●6.模板总结


1.泛型编程

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段;
模板是泛型编程的基础:函数模板 、类模板;

2.函数模板

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。必看!用示例代码学C++模板编程,快速掌握基础知识,高效提升编程能力_泛型编程_02
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。

示例1:函数模板的使用

template <class T1, class T2>
//template <typename T1, typename T2>
void swap(T1 &x, T2 &y)
{
    int tmp = x;
    x = y;
    y = tmp;
}
void test()
{
    int a = 1, b = 2;
    cout << "a=" << a << " " << "b=" << b << endl;//a=1 b=2
    swap(a, b);
    cout << "a=" << a << " " << "b=" << b << endl;//a=2 b=1
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

示例2:函数模板的隐式实例化/显式实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。

template <class T>
T Add(const T &x, const T &y)
{
    return x + y;
}
void test1()
{
    //隐式实例化
    int a = 1;
    double b = 3.14;
    cout << Add(a, (int)b) << endl;    // 4
    cout << Add((double)a, b) << endl; // 4.14
}
void test2()
{
    //显式实例化
    int a = 1;
    double b = 3.14;
    cout << Add<int>(a, b) << endl;// 4
    cout << Add<double>(a, b) << endl;// 4.14
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

示例3:模板参数的匹配原则

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数;
int Add(int x,int y)
{
    cout<<"int Add(int &x,int &y)"<<endl;
    return x+y;
}
template <class T>
T Add(T x, T y)
{
    cout<<"T Add(const T &x, const T &y)"<<endl;
    return x + y;
}
void text()
{
    cout<<Add(1,2)<<endl;
    //int Add(int &x,int &y)
    cout<<Add<int>(1,2)<<endl;
    //T Add(const T &x, const T &y)
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  1. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板;
int Add(int x,int y)
{
    cout<<"int Add(int &x,int &y)"<<endl;
    return x+y;
}
template <class T>
T Add(T x, T y)
{
    cout<<"T Add(const T &x, const T &y)"<<endl;
    return x + y;
}
void text()
{
    cout<<Add(1,2)<<endl;
    //int Add(int &x,int &y)
    cout<<Add<int>(1,2.0)<<endl;
    //T Add(const T &x, const T &y)
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  1. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换;

3.类模板

示例:类模板的使用

template <class T>
class vector
{
public:
    vector(size_t init = 10)
        : _arr(new int[init]), _cap(init), _size(0)
    {
    }
 
    ~vector()
    {
        if (_arr != nullptr)
        {
            delete[] _arr;
            _cap = 0;
            _size = 0;
        }
    }
 
public:
    void push_back(const T &x)
    {
        if (_size == _cap)
        {
            perror("容器已满!");
            exit(0);
        }
        _arr[_size] = x;
        _size++;
    }
    void pop_back()
    {
        if (_size == 0)
        {
            perror("容器为空!");
            exit(0);
        }
        _size--;
    }
 
    const T &front()
    {
        return _arr[0];
    }
    const T& back()
    {
        return _arr[_size-1];
    }
 
private:
    T *_arr;
    size_t _cap;
    size_t _size;
};
 
int main()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    cout << v.front() << endl;//1
    cout<<v.back()<<endl;//4
    v.pop_back();
    cout<<v.back()<<endl;//3
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.

4.非类型模板参数

模板参数分为:类型形参和非类型形参

  1. 类型形参:出现在模板参数类表中,跟在class或者typename之类的参数类型名称之后;
  2. 非类型形参:就是用一个常量作为类模板的一个参数,在类模板中可以将该参数当成一个常量去使用;

示例:

namespace myArray
{
    template<class T,size_t SIZE=100>// 类型形参,非类型形参
    class array{
    public:
        array()
        {
            _size=0;
        }
 
    public:
        void insert(size_t place,T val)
        {
            if(_size==SIZE)
            {
                cout<<"静态数组空间已满!"<<endl;
                return ;
            }
            _arr[place]=val;
            _size++;
        }
        void earse(size_t place)
        {
            if(empty())
            {
                cout<<"数组空间为空!"<<endl;
                return ;
            }
            _size--;
        }
 
        T& operator[](size_t index)
        {
            return _arr[index-1];
        }
        const T& operator[](size_t index) const
        {
            return _arr[index-1];
        }
 
        size_t size()
        {
            return _size;
        }
        bool empty()
        {
            if(_size==0)
                return true;
            else
                return false;
        }
 
    private:
        T _arr[SIZE];
        size_t _size;
    };
}
void test()
{
    myArray::array<int,20> a;
    for(int i=0;i<10;i++)
    {
        a.insert(i,i+1);
        cout<<a[i]<<" ";
    }
    cout<<endl; //0 1 2 3 4 5 6 7 8 9
    cout<<a.size()<<endl;   //10
    for(int i=0;i<10;i++)
    {
        a.earse(i);
    }
    cout<<a.size()<<endl;   //0
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.

5.模板的特化

模板的特化分为:函数模板特化和类模板特化。

  1. 函数模板特化
    1. 必须要先有一个基础的函数模板;
    2. 关键字template后面接一对空的尖括号<>;
    3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型;
    4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误;
    5. 示例:
class Date
{
public:
    Date(int year, int month, int day)
        : _year(year), _month(month), _day(day)
    {
    }
    bool operator<(const Date &d)
    {
        if (this->_year == d._year)
        {
            if (this->_month == d._month)
            {
                if (this->_day < d._day)
                {
                    return true;
                }
                return false;
            }
            return false;
        }
        return false;
    }
 
 
private:
    int _year;
    int _month;
    int _day;
};
template <class T>
bool Less(T left, T right)
{
    return left < right;
}
template <>
bool Less<Date *>(Date *left, Date *right)
{
    return *left < *right;
}
void test()
{
    cout << Less(1, 2) << endl;
    // 1 调用"bool Less(T left,T right)"
 
    Date d1(2021, 12, 1);
    Date d2(2021, 12, 2);
    cout << Less(d1, d2) << endl;
    // 1 调用"bool Less(T left,T right)"
 
    Date *p1 = &d1;
    Date *p2 = &d2;
    // cout<<Less(p1,p2)<<endl;
    // 0 调用"bool Less(T left,T right)"错误
    // 需调用特化版本
    cout << Less(p1, p2) << endl;
    // 1 调用"bool Less<Date*>(Date* left,Date* right)"
 
    // 注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。
    // bool Less(Date * left, Date * right)
    // {
    //     return *left < *right;
    // }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  1. 类模板特化
    1. 示例:
// 全特化
template <class T1, class T2>
class Data
{
public:
    Data() { cout << "Data<T1, T2>" << endl; }
 
private:
    T1 _d1;
    T2 _d2;
};
template <>
class Data<int, char>
{
public:
    Data() { cout << "Data<int, char>" << endl; }
 
private:
    int _d1;
    char _d2;
};
// 偏特化
// 1.部分特化
template <class T1>
class Data<T1, int>
{
public:
    Data() { cout << "Data<T1, int>" << endl; }
 
private:
    T1 _d1;
    int _d2;
};
// 2.参数更进一步的限制
// 两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data<T1 *, T2 *>
{
public:
    Data() { cout << "Data<T1*, T2*>" << endl; }
 
private:
    T1 _d1;
    T2 _d2;
};
// 两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data<T1 &, T2 &>
{
public:
    Data(const T1 &d1, const T2 &d2)
        : _d1(d1), _d2(d2)
    {
        cout << "Data<T1&, T2&>" << endl;
    }
 
private:
    const T1 &_d1;
    const T2 &_d2;
};
void test()
{
    Data<int,char> d2;//Data<int, char>
    Data<double,int> d3;//Data<T1, int>
    Data<int*,int*> d4;//Data<T1*, T2*>
    Data<int&,int&> d5(1,3);//Data<T1&, T2&>
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.

6.模板总结

  1. 优点:
    1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生;
    2. 增强了代码的灵活性;
  2. 缺点:
    1. 模板会导致代码膨胀问题,也会导致编译时间变长;
    2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误;

<您的三连和关注是我最大的动力>
🚀 文章作者:张同学的IT技术日记
分类专栏:C++系列