在定义类的时候,继承和组合可以满足大部分需求。但是对于某些场景,比如用来储存对象的容器类,针对不同的储存对象类型,容器类都需要提供相同的代码实现。如果使用类模版,可以将储存对象类型作为参数传给容器类,这样就可以使用一套容器类代码。
类模版语法
类模版的语法如下,<>
包起来的是模版参数列表,可以有多个参数,用逗号分隔。
template <typename T>
类声明
template
:关键字,表示定义模板
typename
:关键字,表示一个类型参数
T
:变量名,其值代表一个类型。在类中使用。该变量名一般用大写表示。
成员函数,也需要以相同的模板声明开头,并且类限定符也要包含模版参数。
template <typename T>
void ClassName<T>::func() {
}
定义类模版
定义一个容器类Stack
。
template <typename T>
class Stack {
private:
T items[10];
int top;
public:
Stack();
bool isEmpty();
};
template <typename T>
Stack<T>::Stack() {
top = 0;
}
template <typename T>
bool Stack<T>::isEmpty() {
return top == 0;
}
使用类模版
有2种使用类模版的方式,编译器会生成一个特定版本的类,这叫做实例化。
- 指定模版的类型,并且用它定义一个或者多个对象。如果没有生成对象,类模板不会实例化。这叫做隐式实例化。
- 使用
template
和类型来定义一个类,不管有没有生成对象,类模版都会实例化。这叫做显示实例化。
示例如下:
template class Stack<std::string>; // 显示实例化
void test6() {
Stack<int> s1; // 隐式实例化Stack<int>
if (s1.isEmpty()) {
std::cout << "Stack<int> s1 is empty: " << std::endl;
}
Stack<double> *s2; // 没有实例化,因为没有生成对象
s2 = new Stack<double>(); // 实例化Stack<double>
delete s2;
}
类模版的声明和实现写在头文件
一般情况下,在hpp
文件声明一个类的成员函数或者成员数据,在cpp
文件实现成员函数。
同样地,在array.hpp
声明一个类模版:
#ifndef array_hpp
#define array_hpp
template <typename T>
class Array {
private:
int count;
public:
Array();
};
#endif
在array.cpp
定义成员函数模版:
template <typename T>
Array<T>::Array() {
count = 0;
}
在main.cpp
使用该类模版,编译器应该生成int
类型的Array
类,并且使用构造函数创建一个对象a
。
#include <iostream>
#include "array.hpp"
int main(int argc, const char * argv[]) {
Array<int> a;
return 0;
}
但是,编译这些代码,会发生错误:
Undefined symbol: Array<int>::Array()
这是因为类模版和成员函数模版,并不是类和成员函数。前者是一种指令,告诉编译器如何生成后者。
类模版以及成员函数模版,不能分开编译,一般是将他们都放到hpp
文件。代码就可以正常运行。
#ifndef array_hpp
#define array_hpp
template <typename T>
class Array {
private:
int count;
public:
Array();
};
template <typename T>
Array<T>::Array() {
count = 0;
}
#endif
阅读原文,关注公众号[空与一],获取更多C++知识。