C++ 函数模板和类模板
2020 年 06 月 10 日 301 1119 字 暂无评论

前言:

​ C++提供了函数模板(function template)。所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。

  1. C++提供两种模板机制:函数模板、类模板
  2. 类属——类型参数化,又称为参数模板

总结:

  • 模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属。
  • 模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。

01.函数模板

1.1为什么要有函数模板

例如:

需求:写n个函数,交换char类型、int类型、double类型等变量的值

  • 不用函数模板
#include <iostream>
using namespace std;

void myswap(int &a, int &b)
{
    int t = a;
    a = b;
    b = t;
}
 
void myswap(char &a, char &b)
{
    char t = a;
    a = b;
    b = t;
}
  • 用函数模板
#include <iostream>
using namespace std;
//template 关键字告诉C++编译器 我要开始泛型了.你不要随便报错  
//数据类型T 参数化数据类型
template <typename T>
void myswap(T &a, T &b)
{
    T t;
    t = a;
    a = b;
    b = t;
}
 
void main()
{
    int  x = 1;
    int     y = 2;
    myswap(x, y); //自动数据类型 推导的方式 
 
    float a = 2.0;
    float b = 3.0;
 
    myswap(a, b); //自动数据类型 推导的方式 
    myswap<float>(a, b); //显示类型调用 
 
    cout<<"hello..."<<endl;
    system("pause");
    return ;
}

1.2函数模板语法

  • 函数模板定义形式
    • template <类型形式参数表>
  • 类型形式参数的形式为:

    • typename T1 , typename T2 , …… , typename Tn
    • 或 class T1 , class T2 , …… , class Tn

  • 函数模板调用
    • myswap<int>(a,b); //显示类型调用
    • myswap(a,b); //自动数据类型推导

1.3函数模板做函数参数

例子:

#include <iostream>
using namespace std;
template<typename T, typename T2>
void sortArray(T *a, T2 num)
{
        T tmp ;
        int i, j ;
        for (i=0; i<num; i++)
        {
                for (j=i+1; j<num; j++)
                {
                        if (a[i] < a[j])
                        {
                                tmp = a[i];
                                a[i] = a[j];
                                a[j] = tmp;
                        }
                }
        }
}
 
template<class T>
void pirntArray(T *a, int num)
{
        int i = 0;
        for (i=0; i<num; i++)
        {
                cout<<a[i]<<" ";
        }
}
 
void main()
{
        int num = 0;
        char a[] = "ddadeeettttt";
        num = strlen(a);
 
        printf("排序之前\n");
        pirntArray<char>(a, num);
 
        sortArray<char, int>(a, num); //显示类型调用 模板函数 <>
        printf("排序之后\n");
        pirntArray<char>(a, num);
        cout<<"hello..."<<endl;
        system("pause");
return ;
}

1.4函数模板与函数重载

  • 函数模板和普通函数区别
    • 函数模板不允许自动类型转化
    • 普通函数能够进行自动类型转换
  • 函数模板和普通函数一起,调用规则
    • 函数模板可以像普通函数一样被重载
    • C++编译器优先考虑普通函数
    • 如果函数模板可以产生一个更好的匹配,那么选择模板
    • 可以通过空模板实参列表的语法限定编译器只通过模板匹配

1.5函数模板机制结论

  • 编译器并不是把函数模板处理成能够处理任意类的函数
  • 编译器从函数模板通过具体类型产生不同的函数
  • 编译器会对函数模板进行两次编译
    • 在声明的地方对模板代码本身进行编译
    • 在调用的地方对参数替换后的代码进行编译

02.类模板

2.1为什么需要类模板

  • 类模板与函数模板的定义和使用类似
  • 有多个类功能相同,数据类型不同时

  • 类模板用于实现类所需数据的类型参数化
  • 类模板在表示数组、表、图等数据结构特别重要

2.2单个类模板语法

//类的类型参数化 抽象的类
//单个类模板
template<typename T>
class A 
{
public:
    A(T t)
    {
        this->t = t;
    }
 
    T &getT()
    {
        return t;
    }
protected:
private:
    T t;
};

void main()
{
   //模板了中如果使用了构造函数,则遵守以前的类的构造函数的调用规则
    A<int>  a(100); 
    std::cout << a.getT();
}

2.3继承中的类模板语法

  • 子类从模板继承的时候,需要让编译器知道,父类的数据具体是什么(数据类型的本质:固定大小内存块的别名)
class B : public A<int>
{
public:
    B(int i) : A<int>(i)
    {
 
    }
    void printB()
    {
        cout<<"A:"<<t<<endl;
    }
protected:
private:
};
  • 模板与上继承
    • 若基类只有一个带参数的构造函数,子类如何启动父类的构造函数
void pintBB(B &b)
{
        b.printB();
}
void printAA(A<int> &a)  //类模板做函数参数 
{
        a.getT();
}
 
void main()
{
        A<int>  a(100); //模板了中使用了构造函数,则遵守以前的类的构造函数的调用规则 
        a.getT();
        printAA(a);
 
        B b(10);
        b.printB();
}

2.4类模板函数写在类的外部

  • 一般不把类函数写在类内部
  • 构造函数
  • 普通函数
  • 友元函数:用友元函数重载<< >>
friend ostream& operator<< <T> (ostream &out, Complex<T> &c3) ;
  • 友元函数不是实现函数重载
    • 需要在类前增加 类前置声明 函数前置声明
template<typename T>
class Complex;

template<typename T>
Complex<T> mySub(Complex<T> &c1, Complex<T> &c2);
  • 类的内部声明必须写成
friend Complex<T> mySub <T> (Complex<T> &c1, Complex<T> &c2);
  • 友元函数调用必须写成
Complex<int> c4 = mySub<int>(c1, c2);
    cout<<c4;

例子

#include <iostream>

using namespace std;

template <typename T>
class Complex
{
public:
    Complex(T,T);
    ~Complex();
    void printCom()
    {
        cout << "a:" << a << "b:" << b << endl;
    }
    Complex operator+ (Complex& c2);
    Complex operator- (Complex& c2);
private:
    T a;
    T b;
};

template <typename T>
Complex<T>::Complex(T a,T b)
{
    this->a = a;
    this->b = b;
}

template <typename T>
Complex<T>::~Complex()
{
}

template <typename T>
Complex<T>  Complex<T>::operator+ (Complex<T>& c2)
{
    Complex tmp(a + c2.a, b + c2.b);
    return tmp;
}

template <typename T>
Complex<T>  Complex<T>::operator- (Complex<T>& c2)
{
    Complex tmp(a - c2.a, b - c2.b);
    return tmp;
}

void main()
{
    Complex<int> c1(1, 2);
    Complex<int> c2(2, 4);
    c1 = c1.operator+(c2);
    c1.printCom();
}

2.5类模板中的static关键字

  • 从类模板实例化的每个模板类有自己模板数据成员,该模板类的所有对象共享一个static数据成员
  • 和非模板类的static数据成员一样,模板类的static数据成员应该在文件范围定义和初始化
  • 每个模板类有自己的类模板的static数据成员副本


版权属于:zfh

本文链接:http://zfhblog.com/index.php/archives/88/



评论已关闭