C++新增的array采用的就是第二种方法:

代码语言:javascript

AI代码解释

// 定义一个模板类型的静态数组
template<class T, size_t N = 10>
class array
{
public:
	T& operator[](size_t index) { return _array[index]; }
	const T& operator[](size_t index)const { return _array[index]; }
 
	size_t size()const { return _size; }
	bool empty()const { return 0 == _size; }
 
private:
	T _array[N];
	size_t _size;
};

我们可以看到上面的代码中,class关键字(或者是等价的typename)指定T是类型参数,但是后面的size_t指定N是一个无符号整数。这种参数(指定了数据类型而不是使用泛型名称)就叫做非类型参数(nontype)或者是表达式参数

表达式参数的限制:
  • 可以是整型,枚举,引用或指针(所以Double是不被允许的,但是Double*是被允许的)
  • 模板代码不能修改表达式参数的值,也不能使用它的地址(所以不能出现N++、&N等)
  • 在实例化参数模板时,作为表达式参数的值必须是常量参数
表达式参数的优点:

与前面提到的第一中使用构造函数相比,构造函数使用的是new和delete来管理堆内存,这种方法操作消耗大、容易造成内存碎片化、内存泄漏。与之相比,表达式参数的方法使用的是栈内存,这种方法无疑更快,尤其是在处理大量小型数组的时候。

表达式参数的缺点:

使用表达式参数,当模板实例化的时候会为每一个不同的N生成一个类(一份代码):

代码语言:javascript

AI代码解释

template<class T, size_t N>
class Array {
    T data[N];
};

// 不同的N值会产生不同的类
Array<int, 5> arr5;    // 生成 Array<int, 5> 类
Array<int, 10> arr10;  // 生成 Array<int, 10> 类

// 编译器实际生成的代码:
class Array_int_5 {    // Array<int, 5> 的特化
    int data[5];
};

class Array_int_10 {   // Array<int, 10> 的特化  
    int data[10];
};

这样的后果就是:

  • 代码膨胀
  • 编译时间增加
  • 二进制文件体积增大

而相比之下,使用构造函数的方法(动态数组)就显得更加灵活。

2. 模板的特化

2.1. 什么是模板特化?

我们知道,模板的使用就是为了代码的复用率更高,也就是说写一些与数据类型无关的代码,单是不能避免的是,对于一些特殊的类型就可能会得到一下错误的结果,我们通过一个例子来进行说明:

代码语言:javascript

AI代码解释

template<class T>
bool Less(T left, T right)
{
	return left < right;
}
 
int main()
{
	cout << Less(1, 2) << endl;//可以比较
 
	return 0;
}

运行结果:

除了内置类型,自定义类型也是可以通过模板来进行比较,例如我们之前实现的Date日期类:

但是我们来看一下下面这段代码还能够输出正确的结果吗?

代码语言:javascript

AI代码解释

Date d1(2025, 9, 21);
Date d2(2025, 9, 20);

Date* p1 = &d1;
Date* p2 = &d2;

cout << Less(p1, p2) << endl;

很显然它返回了一个错误的结果,因为p1指向的对象d1明显大于p2指向的对象d2,但是却依旧返回了true,这是因为传入的是p1和p2,此时比较的是两者的指针地址。那么模板特化就有了用武之地,模板特化就是在原有的模板基础上,针对特殊的类型进行特殊化的实现方式,可分为函数模板特化、类模板特化。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐