C++ 类模板
一、为什么要有类模板?
比如要写栈、数组、链表容器:
你需要 int 栈、double 栈、string 栈......
如果不写模板,要重复写多份几乎一样的类,代码极度冗余。
类模板作用 :用一套类模板骨架 ,支持任意数据类型,编译器根据类型自动生成对应具体类。
函数模板:通用函数
类模板:通用类
二、类模板 基础语法格式
1. 标准头
cpp
template<typename T>
// 或 template<class T> 效果完全一样
class 类名
{
// 成员变量、成员函数都可以用 T 做类型
private:
T val;
public:
void setVal(T v);
T getVal();
};
-
template<typename T>:声明模板参数 -
T:类型占位符,代表任意类型 -
类里所有成员都可以直接用
T
2. 最简完整示例
cpp
#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;
}
关键区别(和函数模板)
函数模板可以自动类型推导;类模板不能自动推导,必须尖括号指定类型 <类型>
三、类模板 成员函数 类外实现(重点必考)
模板类的成员函数写在类外面,格式固定套路:
cpp
// 1. 先写模板头
template<typename T>
// 2. 类名<T> 指明作用域
void Person<T>::setAge(T a)
{
age = a;
}
template<typename T>
T Person<T>::getAge()
{
return age;
}
固定格式口诀
cpp
template<typename T>
返回值 类名<T>::函数名(参数)
完整可直接复制代码:
cpp
#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
cpp
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;
}
五、类模板 模板参数可以带默认类型
给模板参数设置默认类型,不指定就用默认:
cpp
// 默认 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. 全局特化(全类特化)
通用模板对某一个类型单独重写一整套类:
cpp
// 通用类模板
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:函数模板接收模板类
cpp
template<typename T>
void showPerson(Person<T> p)
{
cout << p.getAge() << endl;
}
写法 2:指定固定类型
cpp
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>::; -
支持多模板参数、默认类型参数;
-
特化可以给指定类型单独定制整个类;
-
模板代码不能分离头文件与源文件;
-
原理:用什么类型,编译器才生成对应具体类。