C++ 类模板
一、为什么要有类模板?
比如要写栈、数组、链表容器:
你需要int栈、double栈、string栈……
如果不写模板,要重复写多份几乎一样的类,代码极度冗余。
类模板作用:用一套类模板骨架,支持任意数据类型,编译器根据类型自动生成对应具体类。
函数模板:通用函数
类模板:通用类
二、类模板 基础语法格式
1. 标准头
template<typename T> // 或 template<class T> 效果完全一样 class 类名 { // 成员变量、成员函数都可以用 T 做类型 private: T val; public: void setVal(T v); T getVal(); };template<typename T>:声明模板参数T:类型占位符,代表任意类型类里所有成员都可以直接用
T
2. 最简完整示例
#include <iostream> #include <string> using namespace std; // 定义类模板 template<typename T> class Person { private: T age; public: void setAge(T a) { age = a; } T getAge() { return age; } }; int main() { // 类模板 必须显式指定类型,不能自动推导 Person<int> p1; p1.setAge(20); cout << p1.getAge() << endl; Person<double> p2; p2.setAge(20.5); cout << p2.getAge() << endl; return 0; }关键区别(和函数模板)
函数模板可以自动类型推导;类模板不能自动推导,必须尖括号指定类型<类型>
三、类模板 成员函数 类外实现(重点必考)
模板类的成员函数写在类外面,格式固定套路:
// 1. 先写模板头 template<typename T> // 2. 类名<T> 指明作用域 void Person<T>::setAge(T a) { age = a; } template<typename T> T Person<T>::getAge() { return age; }固定格式口诀
template<typename T> 返回值 类名<T>::函数名(参数)完整可直接复制代码:
#include <iostream> using namespace std; template<typename T> class Person { private: T age; public: void setAge(T a); T getAge(); }; // 类外实现 template<typename T> void Person<T>::setAge(T a) { age = a; } template<typename T> T Person<T>::getAge() { return age; } int main() { Person<int> p; p.setAge(18); cout << p.getAge() << endl; return 0; }四、类模板 多个模板参数
可以同时定义多个类型参数T1, T2
template<typename T1, typename T2> class Pair { private: T1 first; T2 second; public: void set(T1 a, T2 b) { first = a; second = b; } void show() { cout << first << " " << second << endl; } }; int main() { Pair<int, string> p; p.set(100, "DYKP"); p.show(); return 0; }五、类模板 模板参数可以带默认类型
给模板参数设置默认类型,不指定就用默认:
// 默认 T 为 int template<typename T = int> class Array { private: T arr[10]; }; int main() { Array<> a1; // 不写类型,用默认 int Array<double> a2;// 手动指定 double return 0; }六、类模板的实例化原理
类模板本身不是类,只是一张模板图纸;
当你写
Person<int>、Person<double>时,编译器才会生成对应的具体类;每一种类型,都会产生一份独立的类代码;
没有使用的类型,不会生成代码,不占空间。
七、类模板特化(重点难点)
1. 全局特化(全类特化)
通用模板对某一个类型单独重写一整套类:
// 通用类模板 template<typename T> class Data { public: void print() { cout << "通用版本" << endl; } }; // 针对 string 类型 全特化 template<> class Data<string> { public: void print() { cout << "string 特化版本" << endl; } };使用时:
Data<int>走通用模板Data<string>走特化版本
2. 局部特化(了解)
多个模板参数时,可以只特化其中一部分,考试一般考全特化即可。
八、类模板 作函数参数怎么传
模板类对象作为函数形参,两种写法:
写法 1:函数模板接收模板类
template<typename T> void showPerson(Person<T> p) { cout << p.getAge() << endl; }写法 2:指定固定类型
void showIntPerson(Person<int> p) { cout << p.getAge() << endl; }九、类模板 常见易错坑(必记)
类模板不能自动推导,必须
类名<类型>类外实现成员函数,必须加
template<T>+类名<T>::模板类
声明和实现不能拆分到 .h 和 .cpp
编译需要看到完整模板代码,否则无法实例化,一般全部写在头文件。
模板里面不能使用不支持该类型的运算(如 T 是 string 却用 ++)
特化版本必须和通用模板同名,格式不能错。
十、函数模板 vs 类模板 核心对比(面试必背)
| 对比 | 函数模板 | 类模板 |
|---|---|---|
| 语法 | template<T>修饰函数 | template<T>修饰类 |
| 推导 | 可自动类型推导 | 不能自动推导,必须指定<T> |
| 实例化 | 调用时生成函数 | 指定类型时生成类 |
| 特化 | 支持函数特化 | 支持类全特化、局部特化 |
| 外部实现 | 普通写法 | 必须带类名<T>:: |
十一、极简背诵总结
类模板用
template<typename T>定义,是通用类图纸;使用必须
类名<类型>,无自动推导;类外写成员函数:必须重写模板头 +
类名<T>::;支持多模板参数、默认类型参数;
特化可以给指定类型单独定制整个类;
模板代码不能分离头文件与源文件;
原理:用什么类型,编译器才生成对应具体类。