ARTICLE · 1041288
C++ 模板多功能性详解
C++ 模板多功能性详解

一、核心概念
C++ 模板的真正威力不仅在于"泛型编程",还在于它能够像普通类一样,被灵活地组合到各种面向对象的设计中。这种特性被称为模板多功能性(Template Versatility)。
具体来说,模板类可以:
用作基类(被继承) 用作组件类(作为其他类的成员) 用作其他模板的类型参数(模板嵌套模板) 递归地使用自身(模板的递归实例化)
这些用法与普通类完全一样,正是这种一致性让模板能无缝融入 C++ 的代码重用体系。
二、三种基本组合方式
1. 模板类作为基类
template <typename T>
classArray {
private:
T entry;
// ...
};
// 继承模板类
template <typename Type>
classGrowArray :public Array<Type> {
// ...
};
2. 模板类作为组件(成员)
template <typename Tp>
classStack {
Array<Tp> ar; // 把 Array 作为 Stack 的一个组件
// ...
};
3. 模板类作为其他模板的类型参数
Array<Stack<int>> asi; // 一个"栈的数组"
注意:C++98 要求两个
>之间必须有空格(写成> >),否则会被解析成右移运算符>>。C++11 起不再需要这个空格。
三、递归使用模板
模板最有趣的用法是递归实例化——用模板本身作为自己的类型参数。
ArrayTP<ArrayTP<int, 5>, 10> twodee;
这一行的含义是:
外层 ArrayTP<..., 10>:一个含 10 个元素的数组内层 ArrayTP<int, 5>:每个元素是一个含 5 个 int 的数组
它等价于常规的二维数组:
int twodee[10][5];
重要细节:在模板语法中,维度顺序与等价的二维数组相反。常规写法
twodee[10][5]先写行数(10),后写列数(5);而模板写法先写内层(列,5),后写外层(行,10)。
四、完整代码示例
下面给出一个可编译运行的完整程序,对应程序清单 14.18 的思路:用递归模板创建二维数组,并同时用一维数组保存每行的总和与平均值。
// arraytp.h —— 模板类头文件
#ifndef ARRAYTP_H_
#define ARRAYTP_H_
#include<iostream>
#include<cstdlib>
template <classT, intn>
classArrayTP {
private:
T ar[n];
public:
ArrayTP() {}
explicitArrayTP(const T & v);
virtual T & operator[](int i);
virtual T operator[](int i) const;
};
template <classT, intn>
ArrayTP<T, n>::ArrayTP(const T & v) {
for (int i = 0; i < n; i++)
ar[i] = v;
}
template <classT, intn>
T & ArrayTP<T, n>::operator[](int i) {
if (i < 0 || i >= n) {
std::cerr << "Error in array limits: " << i
<< " is out of range\n";
std::exit(EXIT_FAILURE);
}
return ar[i];
}
template <classT, intn>
TArrayTP<T, n>::operator[](int i) const {
if (i < 0 || i >= n) {
std::cerr << "Error in array limits: " << i
<< " is out of range\n";
std::exit(EXIT_FAILURE);
}
return ar[i];
}
#endif
// twod.cpp —— 主程序
#include<iostream>
#include"arraytp.h"
intmain(){
// 递归使用模板:10 行 5 列的二维数组
ArrayTP<ArrayTP<int, 5>, 10> twodee;
// 一维数组,用于分别保存每行的总和与平均值
ArrayTP<int, 10> sums;
ArrayTP<double, 10> aves;
int i, j;
// 填充二维数组并计算总和
for (i = 0; i < 10; i++) {
int sum = 0;
for (j = 0; j < 5; j++) {
twodee[i][j] = (i + 1) * (j + 1); // 赋值
sum += twodee[i][j];
std::cout.width(2); // 宽度 2
std::cout << twodee[i][j] << ' ';
}
sums[i] = sum;
aves[i] = static_cast<double>(sum) / 5;
std::cout << ": sum = ";
std::cout.width(3);
std::cout << sums[i] << ", average = " << aves[i] << '\n';
}
std::cout << "\nDone.\n";
return0;
}
运行结果(节选):
1 2 3 4 5 : sum = 15, average = 3
2 4 6 8 10 : sum = 30, average = 6
3 6 9 12 15 : sum = 45, average = 9
...
10 20 30 40 50 : sum = 150, average = 30
Done.
要点解析:
ArrayTP<ArrayTP<int, 5>, 10>通过递归实例化得到"10 行 5 列"的结构,写法上列维度在前、行维度在后。twodee[i][j]的两次operator[]调用:第一次返回ArrayTP<int,5>&,第二次返回int&。std::cout.width(2)用来设置下一输出项的字段宽度(只对紧接着的一个输出有效),让对齐更整齐。
五、小结
class D : public Array<T> | ||
Array<Tp> ar; | ||
Array<Stack<int>> | ||
ArrayTP<ArrayTP<int,5>,10> |
模板多功能性说明:模板类本质上仍是类,凡是普通类能参与的设计模式(继承、组合、嵌套、递归),模板类同样能参与。这正是 C++ 模板从"泛型工具"进化为"通用代码重用机制"的关键所在。掌握这一点,就能用同一套模板构造出栈、队列、矩阵、稀疏数组乃至更复杂的数据结构。