磨洋工

Cpp提供很多特性,可以磨洋工代码重用,最重要的就是:

  • 继承
  • 泛型编程

泛型编程

比如说交换两个变量

整形:

void swap(int& x, int& y)
{
    int tmp = x;
    x = y;
    y = tmp;
}

double:

void swap(double& x, double y)
{
    double tmp = x;
    x = y;
    y = tmp;
}

能否只写一个swap()实现各种类型呢?

cpp的创造者就提出了这样高级的方法。

函数模板

定义

就是解决问题的办法,其效果如下

template<class 类型参数1, class 类型参数2, ... >
返回值类型 模板名(形参表)
{
    函数体;
}

实现swap()

template<class T>
void Swap(T& x,T& y)
{
    T tmp = x;
    x = y;
    y = tmp;
}

至于说这是怎么实现的?

啊,是编译器帮助了我们。编译器会自动生成特定类型的函数,比如说编译器检测到了int,那么它就会依据模板自动生成一个把T替换为int的函数。

这个过程叫做模板实例化

例子一

  • 求数组最大元素

    template<class T>
    T maxElement(T a[], int size)
    {
        T tmp = a[0];
        for(int i=1;i<size;++i)
            if(tmp < a[i])
                tmp = a[i];
        return tmp;
    }

强制指定类

模板实例化是由编译器自动推导得来的,有些时候推到的可能不能实现一些功能,比如说下面的代码

template <class T>
T Inc(T n)
{
    return 1+n
}

int main()
{
    cout << Inc<double>(4)/2;    //输出2.5
}

如果我调用时不输入<double>,那么T会变成int,就会输出2了。

重载

只要形参表类型参数表不同即可

template<class T1,class T2>
void print(T1 arg1, T2 arg2)
{
    cout << arg1 << " " << arg2;
}
template<class T> 
void print(T arg1, T arg2)
{
    cout << arg1 << " " << arg2 << endl;    
}

template<class T, class T2>
void print(T arg1, T arg2)
{
    cout << arg1 << " " << arg2 << endl;
}

注意

  1. 普通函数优先于模板函数

    如果模板函数也没有,再报错

  2. 匹配模板函数时,不会经行类型自动转换

    比如说参数是doubleint,那么模板类必须留两个类型才行

    Code

    template<class T>
    T myFunction(T arg1, T arg2)
    {
        cout << arg1 << " " << arg2 << "\n";
        return arg1;
    }
    
    myFunction(5,7);    //ok
    myFunction(5.8,8.4);    //ok
    myFunction(5,8.4);    //error, no matching

例子二

#include <iostream>
using namespace std;

//把区间 [s,e) 上的所有元素经行 op 操作,保存到 x 中
//函数指针也是一种类型哦,不要忘了
template<class T, class Pred>
void Map(T s, T e, T x, Pred op)
  {
    for(;s!=e;++s,++x)  *x = op(*s);
  }

int Cube(int x)
  {
    return x*x*x;
  }

double Square(double x)
  {
    return x*x;
  }

int a[5] = {1,2,3,4,5}, b[5];
double d[5] = {1.1, 2.1, 3.1, 4.1, 5.1}, c[5];

int main()
  {
    Map(a,a+5,b,Square);
    for(int i=1;i<5;++i)  cout << b[i] << ',';
    cout << endl;

    Map(a,a+5,b,Cube);
    for(int i=1;i<5;++i)  cout << b[i] << ',';
    cout << endl;

    Map(d,d+5,c,Square);
    for(int i=1;i<5;++i)  cout << c[i] << ',';
    cout << endl;    

    return 0;
  }

类模板

定义

为了多快好省的定义一批相同的类,可以定义类模板,然后由类模板生成不同的类。

常见的有:

  • 可变长数组

    比如说要实现doubleint,...,无数种类的可变长数组,他们之间除了内部的元素类型不同外,其它的都相同。此时用类模板就非常的方便

类的定义:

template<typename 类型1, typename 类型2...>
class 模板名
{
    成员函数和成员变量;
}
模板声明中的typenameclass关键字等价

成员函数在外面的定义:

template<class 类型1, class 类型2>
返回值 类模板名 <类型参数名列表> :: 成员函数名(参数表)
{
    //your code here
}

实例化的方法:

类模板名 <真实类型参数表> 对象名(构造函数参数表);

例子

实现pair类

#include <iostream>
using namespace std;

template <class T1, class T2>
class Pair
  {
    public:
      T1 key;
      T2 value;
      Pair(T1 k, T2 v):key(k),value(v){};
      bool operator < (const Pair<T1,T2> &p) const;
  };

template<class T1,class T2>
bool Pair<T1, T2>::operator < (const Pair<T1,T2>& p) const
  {
    return key< p.key;
  }

int main()
  {
    Pair<string, int> student("Tom", 19);
    cout << student.key << " " << student.value;
    return 0;
  }

函数模板作为类模板的成员

一定要重新写template<class T>,否则会出玄学锅

看代码

template <class T>
class A
  {
    public:
      template<class T2>
      void Func(T2 t){cout << t;} //成员函数模板
  };

int main()
  {
    A<int> a;
    a.Func('K');
    a.Func("Hello");
    return 0;
  }

非类型参数

模板类的<类型参数表>种,可以有非类型的参数,看代码

template <class T, int size>
class CArray
{
        T array[size];
       public:
        void Print()
        {
            for(int i=0;i<size;++i) cout << array[i] << endl;
        }
};

PY 关系

类模板与派生

类模板当然也是支持派生的,有下面四种情况:

  • 类模板从类模板派生

    Code

    #include <iostream>
    using namespace std;
    
    template <class T1, class T2>
    class A
      {
        T1 v1; T2 v2;
      }; 
    
    template <class T1, class T2>
    class B: public A<T2, T1>
      {
        T1 v3
        T2 v4;
      };
    
    template <class T>
    class C: public B<T, T>
      {
        T v5;
      };
    
    int main()
      {
        B<int, double> obj1;
        //实例化 B<int, double> 和 A<double, int>
        C<int> obj2;
        //实例化 C<int>, B<int, int>和 A<int, int>
        return 0; 
      }

  • 类模板从模板类派生

    Code

    #include <iostream>
    using namespace std;
    
    template <class T1, class T2>
    class A
      {
        T1 v1; T2 v2;
      }; 
    
    template <class T>
    class B: public A<int, double>
      {
        T v;
      }
    
    
    int main()
      {
        B<char> obj1;
        //实例化 B<char> 和 A<int, double>
        return 0; 
      }

  • 类模板从普通类派生

  • 普通类从模板类派生

类模板和友元

  • 函数,类,类的成员函数作为类的友元
  • 函数模板作为类模板的友元
  • 函数模板作为类的友元
  • 类模板作为类模板的友元

代码:

略,懒得敲了

自己加friend试试

别忘了友元不能继承!

哦对了,类模板的声明的友元是所有模板类的友元,包括所有的模板函数模板类

类模板与静态成员变量

类模板中可以定义静态成员,那么模板类中都包括相同的静态成员

template <class T1> class A {
  private:
    static int cnt;

  public:
    A() { cnt++; }
    ~A() { cnt--; }
    A(A &) { cnt++; }
    static void printCnt() { cout << cnt << endl;}
};

//别忘了初始化
template<> 
int A<int>::cnt = 0;

template<>
int A<double>::cnt = 0;


int main()
{
    A<int> ia;
    A<double> da;
    ia.printCnt();
    da.printCnt();
    return 0;
}

输出

1
1
为啥是1和1?

因为A<int>A<double>不一样啊

Last modification:April 6th, 2020 at 01:36 am
Compared with money, your comment could inspire me more.
相较于钱财,你的留言更能激发我创作的灵感