夜雨聆风学习资料网

ARTICLE · 1041288

C++ 模板多功能性详解

C++ 模板多功能性详解

一、核心概念

C++ 模板的真正威力不仅在于"泛型编程",还在于它能够像普通类一样,被灵活地组合到各种面向对象的设计中。这种特性被称为模板多功能性(Template Versatility)

具体来说,模板类可以:

  1. 用作基类(被继承)
  2. 用作组件类(作为其他类的成员)
  3. 用作其他模板的类型参数(模板嵌套模板)
  4. 递归地使用自身(模板的递归实例化)

这些用法与普通类完全一样,正是这种一致性让模板能无缝融入 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<int5>, 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 <classTintn>
classArrayTP {

private:
    T ar[n];
public:
    ArrayTP() {}
explicitArrayTP(const T & v);

virtual T & operator[](int i);
virtual T   operator[](int i) const;
};

template <classTintn>
ArrayTP<T, n>:
:ArrayTP(const T & v) {
for (int i = 0; i < n; i++)
        ar[i] = v;
}

template <classTintn>
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 <classTintn>
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<int5>, 10> twodee;

// 一维数组,用于分别保存每行的总和与平均值
    ArrayTP<int10> sums;
    ArrayTP<double10> 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++ 模板从"泛型工具"进化为"通用代码重用机制"的关键所在。掌握这一点,就能用同一套模板构造出栈、队列、矩阵、稀疏数组乃至更复杂的数据结构。

相关学习资料