1. 模板编程基础概念
模板是C++中最强大的特性之一,它允许我们编写与数据类型无关的通用代码。我第一次接触模板时,就被它的这种"一次编写,多次使用"的特性深深吸引。想象一下,你只需要写一个排序算法,就能适用于int、float、string等各种数据类型,这难道不是每个程序员梦寐以求的吗?
模板的核心思想其实很简单:将数据类型参数化。就像制作饼干时使用的模具,无论你倒入巧克力面团还是香草面团,出来的都是形状一致的饼干,只是口味不同而已。在C++中,这个"模具"就是模板,而"面团"就是我们实际使用的数据类型。
1.1 函数模板实战
让我们从一个最简单的例子开始 - 交换两个变量的值。在传统方式中,我们需要为每种数据类型都写一个swap函数:
cpp复制void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
void swap(double &a, double &b) {
double temp = a;
a = b;
b = temp;
}
// 还需要为char, string等类型继续写...
这显然很冗余。使用模板,我们可以这样写:
cpp复制template <typename T>
void swap(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}
这个小小的template
注意:typename和class在模板参数中可以互换使用,但typename更直观,特别是在嵌套依赖类型时。
1.2 类模板深度解析
函数模板很强大,但类模板才是真正展现模板威力的地方。以最简单的栈(Stack)为例:
cpp复制template <typename T>
class Stack {
private:
std::vector<T> elements;
public:
void push(const T& element) {
elements.push_back(element);
}
T pop() {
if(elements.empty()) {
throw std::out_of_range("Stack<>::pop(): empty stack");
}
T element = elements.back();
elements.pop_back();
return element;
}
bool empty() const {
return elements.empty();
}
};
使用时:
cpp复制Stack<int> intStack; // 存储int的栈
Stack<std::string> strStack; // 存储string的栈
类模板的实例化是在编译时完成的,这意味着每种类型组合都会生成独立的代码。这也是为什么模板代码通常都放在头文件中的原因 - 编译器需要看到完整的定义才能实例化。
1.3 模板特化技巧
有时候,我们对某些特定类型需要特殊处理。这就是模板特化的用武之地。比如,我们想对char*类型的栈做特殊处理(避免浅拷贝问题):
cpp复制template <>
class Stack<char*> {
private:
std::vector<char*> elements;
public:
void push(char* const & element) {
elements.push_back(strdup(element)); // 深拷贝
}
char* pop() {
if(elements.empty()) {
throw std::out_of_range("Stack<>::pop(): empty stack");
}
char* element = elements.back();
elements.pop_back();
return element;
}
// 析构函数需要释放所有字符串
~Stack() {
for(auto &e
