C++模板


用于算法抽象的模板

下面 swapValues 只支持 int 类型:

void swapValues(int& var1, int& var2)
{
    int temp;

    temp = var1;
    var1 = var2;
    var2 = temp;
}

如果我们想将 swapValues 函数用于 char 变量,可添加以下定义来重载函数名:

void swapValues(char& var1, char& var2)
{
    char temp;

    temp = var1;
    var1 = var2;
    var2 = temp;
}

但这样做明显效率很低,令人不满意,如果要将 swapValues 函数应用于更多类型,必须一次又一次地重复几乎完全一样的函数定义。这会使得代码充斥着大量几乎完全一样的定义。我们认为,以下函数定义适合任何类型的变量。

// 交换var1和var2的值
template<class T>
void swapValues(T& var1, T& var2)
{
    T temp;

    temp = var1;
    var1 = var2;
    var2 = temp;
}

其中 template<class T> 这通常称为 模板前缀,它告诉编译器,后续的定义或函数声明是 模板T类型参数

下面是这个实例的全部演示代码:

// 该程序用于演示一个函数模板
#include <iostream>
using namespace std;

// 交换var1和var2的值
template<class T>
void swapValues(T& var1, T& var2)
{
    T temp;

    temp = var1;
    var1 = var2;
    var2 = temp;
}

int main()
{
    int integer1 = 1, integer2 = 2;
    cout<<integer1<<" "<<integer2<<endl; // 1 2
    swapValues(integer1, integer2);
    cout<<integer1<<" "<<integer2<<endl; // 2 1

    char symbol1 = 'A', symbol2 = 'B';
    cout<<symbol1<<" "<<symbol2<<endl; // A B
    swapValues(symbol1, symbol2);
    cout<<symbol1<<" "<<symbol2<<endl; // B A
	
	return 0;
}

编译器不会真的为函数名称 swapValues 生成针对所有可能类型的定义,只是表现得像是生成了所有函数定义。针对用到的每种类型,编译器都生成单独的函数定义,但不会为没用到的任何类型生成定义。另外,无论为一种类型使用多少次模板,都只为那种类型生成一个定义。

此外,函数模板也可能有多个类型参数。例如,具有两个类型参数(T1T2)的一个函数模板可以像下面这样开头:

template<class T1, class T2>

但大多数函数模板都只需一个类型参数。不能有未使用的模板参数。换言之,指定的每个模板参数都必须在模板函数中用到。

算法抽象是指用一个更常规(泛化)的方式表示算法,忽略完全一致的细节,将重点放在算法本质上。C++设计了许多功能来支持算法抽象,函数模板是其中之一。

下面使用泛型排序函数:

// 该程序用于演示一个函数模板
#include <iostream>
using namespace std;

// 交换var1和var2的值
template<class T>
void swapValues(T& var1, T& var2)
{
    T temp;

    temp = var1;
    var1 = var2;
    var2 = temp;
}

template<class BaseType>
int indexOfSmallest(const BaseType a[], int startIndex, int numberUsed)
{
    BaseType min = a[startIndex];
    int indexOfMin = startIndex;

    for (int index = startIndex + 1; index < numberUsed; index++)
        if (a[index] < min)
        {
            min = a[index];
            indexOfMin = index;
            // min是[startIndex]到a[index]之间最小的元素
        }

    return indexOfMin;
}

template<class BaseType>
void sort(BaseType a[], int numberUsed)
{
    int indexOfNextSmallest;
    for(int index=0; index<numberUsed-1; index++)
    {
        // 将之前的值防在a[index]中
        indexOfNextSmallest = indexOfSmallest(a, index, numberUsed);
        swapValues(a[index], a[indexOfNextSmallest]);
    }
}
int main()
{
    int a[10] = {9,8,7,6,5,1,2,3,0,4};
    for(int i=0; i<10; ++i)
        cout << a[i] << " ";
    cout<<endl;
    sort(a, 10);
    for(int i=0; i<10; ++i)
        cout << a[i] << " ";
    cout<<endl;

    double b[5] = {5.5, 4.4, 1.1, 3.3, 2.2};
    for(int i=0; i<5; ++i)
        cout << b[i] << " ";
    cout<<endl;
    sort(b, 5);
    for(int i=0; i<5; ++i)
        cout << b[i] << " ";
    cout<<endl;

    char c[7] = {'G', 'E', 'N', 'E', 'R', 'I', 'C'};
    for(int i=0; i<7; ++i)
        cout<<c[i]<<" ";
    cout<<endl;
    sort(c, 7);
    for(int i=0; i<7; ++i)
        cout<<c[i]<<" ";
    cout<<endl;
    return 0;
}

如果要使用这个泛型,那么这种类型得可以用 < 比较。比如下面就是未不恰当的类型使用模板:

int a[10], b[10];
<用于填充数组的一些代码>
swapValues(a, b);

上述代码不能工作,因为数组类型不支持赋值。


用于数据抽象的模板

类模板的语法和函数模板基本一样。下面是放在模板定义之前的语句:

template<class T>

类型参数 T 在类定义中的用法与其他任何类型无异。和函数模板一样,类型参数 T 可以是任何类型;类型参数不一定要替换成类类型。和函数模板一样,可用任何(非关键字)标识符代表 T

以下面的类模板为例。该类的对象包含一对 T 类型的值。 如果 Tint,则对象值是一对整数;如果 Tchar,则对象值是一对字符,依此类推。

// 该类表示一对T类型的值:
template<class T>
class Pair
{
public:
	Pair();
	Pair(T firstValue, T secondValue);
	
	void setElement(int position, T value);
	// 前条件:position是1或2
	// 后条件:指定的 position 被设置成 value
 	
 	T getElement(int position) const;
 	// 前条件:position 是 1 或 2
 	// 返回在指定 position 的值
private:
	T first;
	T second;
};

定义好类模板之后就可声明该类的对象。声明必须指定要为 T 填充什么类型。例如,以下语句声明 score 对象来记录一对整数。另外还声明 seats 对象来记录一对字符:

Pair<int> score;
Pair<char> seats;

然后可以像使用其他任何对象那样使用上述两个对象。例如,以下语句将第一个球队的 score 设置为3,将第二个球队的 score 设置成0:

score.setElement(1, 3);
score.setElement(2, 0);

类模板的成员函数采用与普通类的成员函数一样的方式来定义。唯一的区别是,成员函数定义本身也是模板。例如,以下代码给出了成员函数 setElement 的一个恰当的定义,共给出了接收两个参数的构造函数的定义:

// 使用iostream和cstdlib
template<class T>
void Pair<T>::setElement(int position, T value)
{
	if (positon == 1)
		first = value;
	else if (position == 2)
		second = value;
	else
	{
		cout << "Error: Illegal pair position.\n";
		exit(1);
	}	
}

template<class T>
Pair<T>::Pair(T firstValue, T secondValue)
		: first(firstValue), second(secondValue)
{
	// 主体有意留空
}

注意,作用域解析操作符之前的类名是 Pair<T>,而非单单是 Pair

类模板名称可用作函数参数类型。例如,以下语句声明一个函数,参数是一对整数:

int addUp(const Pair<int>& thePair);
// 返回thePair中的两个整数的和

注意,其中指定了要填充的类型(本例是 int),它将取代类型参数 T

甚至可以在函数模板中使用类模板。例如,可以不像上面那样定义具体的 addUp 函数,而是定义函数模板,使函数能支持所有数值类型:

template<class T>
T addUp(const Pair<T>& thePair);
// 前条件已经为T类型的值定义了操作符+
// 返回thePair中的两个值之和
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值