C++的模板理解

      • 1、模板
        • 1.1、模板的概念
        • 1.2、模板的分类
          • 1.2.1、模板函数
          • 1.2.2、模板类
      • 2、非类型模板参数
        • 2.1、定义
        • 2.2、例子
        • 2.3、使用规则

1、模板

1.1、模板的概念

模板就是生成一个通用的函数,这个函数可以接受任意数据类型的参数,可以返回任意类型的值。
模板是泛型编程的基础。所谓泛型编程就是编写与类型无关的逻辑代码,是一种强大的复用方式。

1.2、模板的分类

1.2.1、模板函数
  • 1、书写格式
template1 , class 形参2,...>
返回类型 函数名(形参表)
{
    // ;
}
template1 , typename 形参2,...>
返回类型 函数名(形参表)
{
    // ;
}

模板形参定义class和typename是完全等价的,没有区别,所以你可以任意使用,但是不要两个混搭,让人看着不舒服。

  • 2、代码示例
eg:

void Swap(int *x1,int *x2)
{
    int tmp = *x1;
    *x1 = *x2;
    *x2 = tmp;
}
void Swap(int **x1, int **x2)
{
    int *tmp = *x1;
    *x1 = *x2;
    *x2 = tmp;
}
int main()
{
    int a = 1;
    int b = 2;
    Swap(&a, &b);
    int *p1 = &a;
    int *p2 = &b;
    Swap(&p1, &p2);

    return 0;
}

根据上面例子看出:当你想进行两个整型交换时,是不是要写一个交换函数,当你想交换两个指针,又要写一个交换函数,那么你想交换两个字符串是不是又要写,为了解决这种麻烦,c++有了模板的这个概念,

template
void Swap(T *x1, T* x2)
{
    T tmp =  *x1;
    *x1 = *x2;
    *x2 = tmp;
}

这就是模板,它不关注你具体需要的类型,它会根据你的需求确定不同的函数类型执行不同的代码(这叫做模板函数实例化)。并且在c++库中交换函数swap也是用模板实现的。

重要的一点:编译时不会编译模板函数大括号里面的内容,那是因为模板函数还没有实例化,因为不知道函数形参类型,所以根本不能在堆栈上开辟空间

//实例化调用
void Test()
{
    int a = 1;
    int b = 2;
    Swap(&a,&b);//这叫做隐式实例化调用,需要系统自己推演实例化类型。
    Swap<int>(&a,&b);//这叫做显示实例化,不需要推演就可以直接调用。
}
  • 3、调用规则
    • 3.1:没有显示实例化时,系统会调用默认模板函数
    • 3.2:当既有模板函数又有原函数时时,会直接调用原函数,不会调用模板函数
    • 3.3:在重载函数模板中,显示实例化后会调用最匹配的的模板函数,不再调用默认的模板函数,如果没有找到最匹配的会再调用默认模板函数
    • 3.4:模板参数支持设置缺省值,如果全缺省时,就不再需要显示实例化了。
    • 3.5:缺省规则不遵循从右到左缺省原则
    • 3.6:如果模板设置了默认缺省值并且显示实例化了,系统会使用显示实例化的参数而不是默认参数。
eg:
void show(int a, int b)
{
    cout << "show()的普通函数" << endl;
}
template <class T>
void show(T a,T b)
{
    cout << "默认模板函数"  << endl;
}
template <class T1, class T2>
void show(T1 a,T2 b)
{
    cout << "不同类型的模板函数" << endl;
}

C++的模板理解_第1张图片

template <class T>
void show(T a,T b)
{
    cout << "默认模板函数"  << endl;
}
template <class T1, class T2>
void show(T1 a,T2 b)
{
    cout << "不同类型的模板函数" << endl;
}

C++的模板理解_第2张图片

1.2.2、模板类
  • 1、书写格式
template1, class 形参名2, ...,class 形参名n>
class 类名
{
 //类定义;
};
  • 2、代码示例
template
class A
{
public:
    A()
    {}
protected:
    T _a;
};
int main()
{
    A<int>a;//必须传类的类型,否则在实例化时不能给对象a开辟空间大小
    return 0;
}

2、非类型模板参数

2.1、定义

模板参数不仅可以定义为类型还可以定义为固定类型的常量值。

2.2、例子

模板类与模板函数都可以用非类型形参

template<class T,int n>
void sum(T a,int n)
{
    T i = a;
    T sum = 0;
    for (i=a; i <= n; ++i)
    {
        sum += i;
    }
    cout << sum << endl;
}
template<class T,int MAXSIZE>
class SeqList
{
public :
    SeqList();
private :
    T _array [MASIZE];
    int _size ;
};

void Test()
{
    sum<int, 3>(0, 3);
    SeqList<int,10>S;
}

2.3、使用规则

  • 规则

    • 1:可转化为整型的类型都可以作为非类型模板参数,比如:int、char、short、long、unsigned、bool,指针和引用也可以作为非类型模板参数
    • 2:浮点数不能作为非类型模板形参,比如:float、double
    • 3:类,字符串不可以作为非类型模板参数
    • 4:实参必须是编译时常量表达式,不能使用非const的局部变量,局部对象地址和动态对象
    • 5:非const的全局指针,全局对象,全局变量都不是常量表达式。
    • 6:由于形参的已经做了限定,字符串,浮点型即使是常量表达式也不可以作为非类型实参
template<class T,int MAXSIZE> 
class List{  
private:  
    T array[MAXSIZE];    
public:  
    void show()
    {
    //;
    }
};  

const int num1 = 9; ;//全局变量  
static int num2= 9; ;//全局变量  
void Test()
{
    const int num3 = 9; ;//局部变量  
    List<int,num1> list; //正确  
    List<int,num2> list; //错误  
    List<int,num3> list; //正确  
}


//再看一个关于指针和字符串比较特别的例子   
template<char const* name>  
class pointer
{  
};  
char a[] = "aa";;//全局变量  
char *b = "aa";//全局变量  
char *const c = "aa";//全局变量,顶层指针,指针常量  
void Test()
{
    char d[] = "aa";//局部变量
    pointer<"aa">  p1;//错误  
    pointer  p2;//正确  
    pointer  p4;//错误,error C2975:“pointer”的模板参数无效,应为编译时常量表达式  
    pointer  p5;//错误,error C2970: “c”: 涉及带有内部链接的对象的表达式不能用作非类型参数 
    pointer  p3;//错误,局部变量不能用作非类型参数   
}

你可能感兴趣的:(编程语言-c++)