C++: 模板基础

最后更新:2026-08-26

前面的课程里,我们写的函数或类都只能处理一种类型

但如果你要写一个"能处理 intdoublestringmax 函数呢?

模板就是解决这个问题——让函数和类能支持任意类型


1. 什么是模板?

(1) 1.1 生活中的模板*

生活场景 程序中的对应
做蛋糕的模具(可以倒不同口味的面糊) 模板
Word 邮件合并(一份模板,填入不同收件人) 模板

模板的本质: 让编译器根据使用时的类型,自动生成对应版本的函数或类

(2) 1.2 为什么需要模板?

不用模板(重复代码):

CPP
#include <iostream>

// 处理 int 的 max
int max(int a, int b) {
 return a > b ? a : b;
}

// 处理 double 的 max
double max(double a, double b) {
 return a > b ? a : b;
}
// ... 要写 N 个版本!

用模板(代码复用):

CPP
#include <iostream>

// 函数模板
template <typename T>
T max(T a, T b) {
 return a > b ? a : b;
}

int main() {
 std::cout << max(3, 5) << std::endl; // 编译器自动生成 int 版本
 std::cout << max(3.14, 2.72) << std::endl; // 编译器自动生成 double 版本
 return 0;
}


2. 函数模板*

(1) 2.1 基本语法*

CPP
template <typename TypeName>
ReturnType functionName(parameterList) {
 // function body
}

💡 重点: typename 也可以写成 class(两者等价),但推荐用 typename(更直观)。

▶ 示例 1:函数模板(难度⭐)

CPP
#include <iostream>

// 函数模板
template <typename T>
T max(T a, T b) {
 return a > b ? a : b;
}

int main() {
 std::cout << "max(3, 5) = " << max(3, 5) << std::endl;
 std::cout << "max(3.14, 2.72) = " << max(3.14, 2.72) << std::endl;
 
 // 也可以显式指定类型
 std::cout << "max<int>(3, 5) = " << max<int>(3, 5) << std::endl;
 
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
max(3, 5) = 5
max(3.14, 2.72) = 3.14
max<int>(3, 5) = 5


3. 类模板*

(1) 3.1 基本语法*

CPP
template <typename TypeName>
class ClassName {
 // members
};

▶ 示例 2:类模板 Pair(难度⭐⭐)

CPP
#include <iostream>
#include <string>

// 类模板:存一对值
template <typename T1, typename T2>
class Pair {
private:
 T1 first;
 T2 second;
 
public:
 Pair(const T1& a, const T2& b) : first(a), second(b) {}
 
 void print() {
 std::cout << "(" << first << ", " << second << ")" << std::endl;
 }
};

int main() {
 Pair<int, double> p1(3, 3.14);
 Pair<std::string, int> p2("Alice", 20);
 
 p1.print(); // (3, 3.14)
 p2.print(); // (Alice, 20)
 
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
(3, 3.14)
(Alice, 20)

💡 重点: 类模板使用时必须显式指定类型(除非构造函数能推导)。



4. 模板的声明与定义*

(1) 4.1 问题:模板应该放哪里?*

如果你把模板的声明放 .h,定义放 .cpp,会链接错误

原因: 模板不是真正的函数/类,它是编译时生成代码的"模具"。

(2) 4.2 解决方法:声明和定义放一起*

推荐写法: 把模板的声明和定义都放 .h 文件里。

CPP
// pair.h(头文件)
#ifndef PAIR_H
#define PAIR_H

template <typename T1, typename T2>
class Pair {
private:
 T1 first;
 T2 second;
 
public:
 Pair(const T1& a, const T2& b);
 void print();
};

// 定义也放这里!
template <typename T1, typename T2>
Pair<T1, T2>::Pair(const T1& a, const T2& b) : first(a), second(b) {}

template <typename T1, typename T2>
void Pair<T1, T2>::print() {
 std::cout << "(" << first << ", " << second << ")" << std::endl;
}

#endif

💡 提示: 这是模板和普能函数/类最大的区别——模板通常声明和定义放一起



5. 实战:简单数组模板*

▶ 示例 3:实现 Array 类模板(难度⭐⭐⭐)

CPP
#include <iostream>
#include <cassert>

template <typename T>
class Array {
private:
 T* data;
 int size;
 
public:
 Array(int sz) : size(sz) {
 data = new T[size];
 }
 
 ~Array() {
 delete data;
 }
 
 // 拷贝构造函数(深拷贝)
 Array(const Array& other) : size(other.size) {
 data = new T[size];
 for (int i = 0; i < size; i++) {
 data[i] = other.data[i];
 }
 }
 
 // 重载 运算符
 T& operator(int index) {
 assert(index >= 0 && index < size);
 return data[index];
 }
 
 int getSize() const {
 return size;
 }
};

int main() {
 Array<int> arr(5);
 for (int i = 0; i < arr.getSize(); i++) {
 arr[i] = (i + 1) * 10;
 }
 
 for (int i = 0; i < arr.getSize(); i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
10 20 30 40 50 

▶ 示例 3:函数模板(难度⭐)

CPP
#include <iostream>

template<typename T>
T getMax(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    std::cout << "int最大:" << getMax(3, 5) << std::endl;
    std::cout << "double最大:" << getMax(3.14, 2.71) << std::endl;
    std::cout << "string最大:" << getMax("apple", "banana") << std::endl;

    return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
int最大:5
double最大:3.14
string最大:banana
💡 提示:模板让函数适用于多种类型,编译器自动推导 T 的实际类型。


Q:模板和函数重载有什么区别? A:> - 函数重载:多个同名函数,参数类型不同(编译时确定调用哪个) > - 函数模板:一个"模具",编译器根据使用时的类型自动生成对应版本 > > 推荐: 如果逻辑完全一样(只是类型不同),用模板;如果逻辑不同,用重载。 Q:模板能支持所有类型吗? A:不能!模板要求类型支持某些操作。 > > template <typename T> > T add(T a, T b) { > return a + b; // 要求 T 支持 operator+ > } > > // 如果 T 是自定义类,但没有重载 operator+,会编译报错。 > Q:什么时候该用模板? A:当你的函数/类的逻辑和类型无关时。 > > 典型场景: > - 容器类(std::vectorstd::array) > - 算法(std::sortstd::find) > - 工具函数(std::maxstd::minstd::swap


Q:关于templates basics最重要的是什么? A:先理解核心概念,再通过实践例子来巩固。


❓ 常见问题

Q 模板声明和定义必须放在头文件吗?
A 是的,模板在编译时需要完整定义,通常放在 .h 文件中,或者用 export 关键字(C++20)。
Q 类模板和函数模板有什么区别?
A 函数模板可以自动推导类型参数,类模板必须显式指定类型参数。

📖 小节


📝 作业

  1. 基础题 (Difficulty ⭐): 写一个函数模板 T min(T a, T b),返回两个值中的较小值。

  2. 进阶题 (Difficulty ⭐⭐): 写一个类模板 BoxT,包含一个 T 类型的成员,以及 void set(T val)T get() 成员函数。

  3. 挑战题 (Difficulty ⭐⭐⭐): 扩展上面的 Array 类模板,添加:

  4. void push_back(const T& val)(动态扩容)

  5. void pop_back()

  6. 用模板实现,让它能存任意类型

6. 🚀 下一步*

学会了模板基础,接下来我们学习 STL 容器(第34课)—— 用 C++ 标准库提供的现成容器,不用自己造轮子!

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏