《程序设计实习》之【模板】

函数模板

泛型程序设计
  • Generic Programmin
  • 算法实现时不指定具体要操作的数据的类型
  • 泛型 —— 算法实现一遍 -> 适用于多种数据结构
  • 优势:减少重复代码的编写
  • 大量编写模板,使用模板的程序设计
    • -函数模板
    • 类模板
函数模板

为了交换两个int变量的值,需要编写如下Swap函数:

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

为了交换两个double变量的值,还需要编写如下Swap函数:

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

能否只写一个Swap, 就能交换各种类型的变量?用函数模板解决。

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

交换两个变量值的函数模板:

template <class T>
void Swap(T & x, T & y) {
    T tmp = x;
    x = y;
    y = tmp;
}
int main(){
    int n = 1, m = 2;
    Swap(n, m);  //编译器自动生成 void Swap(int &, int &)函数
    double f = 1.2, g = 2.3;
    Swap(f, g);  //编译器自动生成 void Swap(double &, double &)函数
    return 0;
}

函数模板中可以有不止一个类型参数。

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

求数组最大元素的MaxElement函数模板:

template <class T>
T MaxElement(T a[], int size) //size是数组元素个数
{
    T tmpMax = a[0];
    for( int i = 1; i < size; ++i )
        if( tmpMax < a[i] )
            tmpMax = a[i];
    return tmpMax;
}

函数模板可以重载,只要他们的形参表不同即可。
例,下面的两个模板可以同时存在:

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

C++编译器遵循以下优先顺序:

  • Step 1:先找参数完全匹配普通函数(非由模板实例化而得的函数)
  • Step 2:再找参数完全匹配模板函数
  • Step 3:再找实参经过自动类型转换后能够匹配的普通函数
  • Step 4:上面的都找不到,则报错
例:函数模板调用顺序
template <class T> 
T Max(T a, T b) {
    cout << "Template Max 1" << endl;
    return 0; 
} 
template <class T, class T2> 
T Max(T a, T2 b) {
    cout << "Template Max 2" <<endl;
    return 0; 
}
double Max(double a, double b) {
    cout << "MyMax" << endl;
    return 0;
}
int main() {    
    int i=4, j=5;
    Max(1.2, 3.4);  // 调用Max(double, double)函数
    Max(i, j);      // 调用第一个T Max(T a, T b)模板生成的函数
    Max(1.2, 3);    // 调用第二个T Max(T a, T2 b)模板生成的函数
    return 0;
}

运行结果:

MyMax
Template Max 1
Template Max 2

赋值兼容规则引起函数模板中类型参数的二义性。

template<class T>
T myFunction(T arg1, T arg2) {
    cout<< arg1 <<“ ”<<arg2<<“\n”;
    return arg1;
}
…

myFunction(5, 7);  //ok: replace T with int
myFunction(5.8, 8.4);  //ok: replace T with double

myFunction(5, 8.4);  //error: replace T with int or double? 二义性

可以在函数模板中使用多个类型参数, 可以避免二义性。

template<class T1, class T2>
T1 myFunction( T1 arg1, T2 arg2) {
    cout<<arg1<<“ ”<<arg2<<“\n”;
    return arg1;
}

…

myFunction(5, 7);     //ok:replace T1 and T2 with int
myFunction(5.8, 8.4); //ok: replace T1 and T2 with double
myFunction(5, 8.4);   //ok: replace T1 with int, T2 with double

类模板

  • 对于这些数组类
    • 除了元素的类型不同之外, 其他的完全相同
  • 类模板
    • 在定义类的时候给它一个/多个参数
    • 这个/些参数表示不同的数据类型
  • 在调用类模板时, 指定参数, 由编译系统根据参数提供的数据类型自动产生相应的模板类
类模板的定义
  • C++的类模板的写法如下:
template <类型参数表>
class 类模板名 {
    成员函数和成员变量
};
  • 类模板里的成员函数, 如在类模板外面定义时
template <型参数表>
返回值类型 类模板名<类型参数名列表>::成员函数名(参数表) {
    ……
}
  • 用类模板定义对象的写法如下
类模板名 <真实类型参数表> 对象名(构造函数实际参数表);
  • 如果类模板有无参构造函数, 那么也可以只写
类模板名 <真实类型参数表> 对象名;
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;
};

// Pair的成员函数 operator <
template<class T1, class T2>
bool Pair<T1,T2>::operator<( const Pair<T1, T2> & p) const {
    return key < p.key;
}

Pair类模板的使用:

int main() {
    Pair<string, int> student("Tom", 19);  //实例化出一个类 Pair<string, int>
    cout << student.key << " " << student.value;
    return 0;
}

输出结果:

Tom 19
实用类模板声明对象
  • 编译器由类模板生成类的过程叫类模板的实例化
    • 编译器自动用具体的数据类型替换类模板中的类型参数, 生成模板类的代码
  • 由类模板实例化得到的类叫模板类
    • 为类型参数指定的数据类型不同, 得到的模板类不同

同一个类模板的两个模板类是不兼容的。

Pair<string, int> * p;
Pair<string, double> a;
p = & a; //wrong
函数模板作为类模板成员
#include <iostream>
using namespace std;
template <class T>
class A {
public:
    template<class T2>
    void Func(T2 t) {
        cout << t << endl;    // 成员函数模板
    }
};
int main() {
    A<int> a;
    a.Func('K');    // 成员函数模板Func被实例化
    return 0;
}

程序输出:

K

若函数模板改为

template <class T>
void Func(T t){ cout << t << endl; }

将报错 “declaration of ‘class T’ shadows template parm ‘class T’ ”

类模板与非类型参数
  • 类模板的参数声明中可以包含非类型参数
template <class T, int elementsNumber>
- 非类型参数: 用来说明类模板中的**属性**
- 类型参数: 用来说明类模板中的**属性类型**, 成员操作的参数类型和返回值类型
template <class T, int size>
class CArray{
    T array[size];
public:
    void Print( ) {
        for(int i = 0; i < size; ++i)
            cout << array[i] << endl;
    }
};
CArray<double, 40> a2;
CArray<int, 50> a3;

注意:

CArray<int,40>CArray<int,50>完全是两个类,这两个类的对象之间不能互相赋值。

类模板与继承
  • 类模板派生出类模板
  • 模板类(即类模板中类型/非类型参数实例化后的类)派生出类模板
  • 普通类派生出类模板
  • 模板类派生出普通类

(1)类模板从类模板派生

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;
    C<int> obj2;
    return 0;
}

实例化过程如下:

class B<int, double>:public A<double, int>{
    int v3; double v4;
};
class A<double, int> {
    double v1; int v2;
};
class C<int>:public B<int int> {
    int v5;
}
class B<int, int>:public A<int, int>{
    int v3; int v4;
};
class A<int, int> {
    int v1; int v2;
};

(2)类模板从模板类派生

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;
    return 0;
}

自动生成两个模板类:A<int, double>B<char>

(3)类模板从普通类派生

class A {
    int v1; 
};

template <class T>
class B:public A {
    T v;
};
int main() {
    B<char> obj1;
    return 0;
}

(4)普通类从模板类派生

template <class T>
class A { 
    T v1;
    int n; 
};

class B:public A<int> {
    double v; 
};

int main() {
    B obj1;
    return 0;
}

string类

输入输出

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值