如何将类模板应用于生成?

摘要:基本概念 跟函数模板类似,类模板是用于创建具有相同行为接口(算法一致)但数据类型不同的类的蓝图。 核心逻辑:类的行为(成员函数、操作逻辑)与存储的数据类型无关,仅需定义一次模板,即可适配多种数据类型。 典型示例 链表类、栈类、队列类等容器类
基本概念 跟函数模板类似,类模板是用于创建具有相同行为接口(算法一致)但数据类型不同的类的蓝图。 核心逻辑:类的行为(成员函数、操作逻辑)与存储的数据类型无关,仅需定义一次模板,即可适配多种数据类型。 典型示例 链表类、栈类、队列类等容器类: 核心操作(插入、删除、检索、合并)与存储的数据类型无关 通过类模板可快速实现支持 int、string、自定义类型等多种数据的容器,无需重复编写类代码 类模板的定义 核心关键字 template:声明模板的关键字 typename:指定类型参数(旧标准可用 class,功能一致,推荐用 typename 更清晰) 语法格式 // 类模板定义(支持多个类型参数) template <typename T1, typename T2> // T1、T2 为类型参数(占位符) class node { public: // 成员函数(可直接在类内实现) node(){cout << "通用版本" << endl;} private: T1 a; // 用类型参数定义成员变量 T2 b; }; 关键注意事项 创建对象时,类型参数列表不可省略(与函数模板不同):// 正确:显式指定类型参数 node<short, double> a; node<int, string> b; // 错误:未指定类型参数 // node c; // 编译报错,编译器无法推断类型 类型参数可自定义名称(如 T、Type、Data 等),遵循标识符命名规则 类模板仅为“设计蓝图”,未实例化前不会生成具体代码 类模板的特化 当通用模板无法满足特定类型的需求时,为该类型提供专门的实现版本,称为模板特化。分为「偏特化」和「全特化」。 偏特化(部分类型参数特化) 定义 仅特化类模板中的部分类型参数,剩余参数仍为模板参数。 示例(基于上述 node 模板) // 偏特化1:第一个参数为 string,第二个参数保留模板参数 T template <typename T> class node<string, T> { public: node(){cout << "偏特化(第一个参数为 string)" << endl;} private: string a; T b; }; // 偏特化2:第二个参数为 string,第一个参数保留模板参数 T template <typename T> class node<T, string> { public: node(){cout << "偏特化(第二个参数为 string)" << endl;} private: T a; string b; }; 关键注意事项 特化版本的类声明必须指明已特化的类型(如 <T, string>) 二义性问题:当多个偏特化版本都能匹配同一类型组合时,编译报错// 错误示例:两个偏特化版本都能匹配 node<string, string> int main(void) { node<string, string> n; // 编译报错,二义性 } 报错信息:error: ambiguous template instantiation for 'class node<std::cxx11::basic_string<char>, std::cxx11::basic_string<char>>' candidates are: template<class T1> class node<T1, std::_cxx11::basic_string<char>> template<class T2> class node<std::_cxx11::basic_string<char>, T2> 全特化(所有类型参数特化) 定义 特化类模板中的全部类型参数,无剩余模板参数。 示例 // 全特化:两个参数分别为 double 和 string template <> // 无剩余模板参数,尖括号留空 class node<double, string> { public: node(){cout << "全特化(double + string)" << endl;} private: double a; string b; }; 关键注意事项 全特化版本的匹配优先级最高,不会与偏特化产生二义性:node<double, string> d; // 优先匹配全特化版本,无报错 全特化本质是一个独立的类,仅复用了原模板的类名 模板匹配优先级 全特化版本 → 偏特化版本 → 通用模板版本 类模板的其他核心语法 类模板的静态成员 定义规则 类模板的静态成员属于「模板实例化后的具体类」,而非模板本身 必须在类外显式初始化,且需保留模板参数 示例 // 类模板定义 template <typename T> class A { public: static int count; // 静态成员声明 }; // 静态成员初始化(必须在类外) template <typename T> int A<T>::count = 0; // 语法:模板参数 + 类名::成员名 注意 不同模板实例的静态成员是独立的(如 A<int>::count 和 A<string>::count 互不影响) 初始化语句必须放在头文件中(与类模板方法定义规则一致) 类模板的成员方法定义 两种实现方式 方式1:类内直接实现(推荐,简单场景) template <typename T> class A { public: void print() { cout << "类内实现" << endl; } // 直接定义 }; 方式2:类外实现(复杂场景,需注意语法) // a.h 头文件(必须放在头文件中,不能放 .cpp) template <typename T> class A { private: T *p; public: A(); // 构造函数声明 ~A(); // 析构函数声明 void func(); // 普通成员函数声明 }; // 类外实现构造函数 template <typename T> // 需重复模板声明 A<T>::A() // 语法:类名<T>::成员名 { p = new T[100]; } // 类外实现析构函数 template <typename T> A<T>::~A() { delete [] p; } // 类外实现普通成员函数 template <typename T> void A<T>::func() { cout << "类外实现" << endl; } 关键语法规则 必须放在头文件中:类模板未实例化前无具体代码,*.cpp 编译时无法生成目标代码,链接时会报错 类外实现需重复 template <typename T> 声明 成员名前必须加 类名<T>:: 限定作用域 不推荐的替代方案(仅作了解) 将模板实现的 .cpp 文件直接包含到使用模板的 main.cpp 中: // main.cpp #include <iostream> #include "a.h" // 类模板声明 #include "a.cpp" // 类模板实现(不推荐,破坏文件结构) 拓展 类模板的参数默认值 C++11 及以上支持为类模板的类型参数指定默认值,类似函数默认参数。 示例 // 为 T2 指定默认值 int template <typename T1, typename T2 = int> class node { public: node(){cout << "默认参数版本" << endl;} private: T1 a; T2 b; // 未指定时默认为 int }; // 使用场景 node<double> obj1; // 等价于 node<double, int> node<string, double> obj2; // 显式指定第二个参数,覆盖默认值 类模板的继承 场景1:模板类继承普通类 class Base {}; // 普通类 template <typename T> class Derived : public Base // 模板类继承普通类 { private: T data; }; 场景2:普通类继承模板类(需显式指定模板参数) template <typename T> class Base {}; // 模板类 class Derived : public Base<int> // 必须指定模板参数(如 int) {}; 场景3:模板类继承模板类 template <typename T1> class Base {}; // 父模板类 template <typename T1, typename T2> class Derived : public Base<T1> // 子模板类复用父模板的参数 { private: T2 data; }; 类模板与友元 示例1:普通友元函数 template <typename T> class A { T data; public: A(T d) : data(d) {} // 友元函数(非模板函数) friend void print(A<T> &obj) { cout << obj.data << endl; } }; 示例2:模板友元函数(支持所有类型实例) template <typename T> class A { T data; public: A(T d) : data(d) {} // 模板友元函数 template <typename U> friend void print(A<U> &obj); }; // 友元函数类外实现 template <typename U> void print(A<U> &obj) { cout << obj.data << endl; } 类模板的类型萃取(Type Traits) 核心思想 通过模板特化,在编译期获取类型的属性(如是否为指针、是否为整型、是否为const类型等),实现编译期分支选择。 简单示例(判断是否为指针类型) // 通用模板:默认不是指针 template <typename T> struct IsPointer { static const bool value = false; }; // 特化版本:当 T 为指针时 template <typename T> struct IsPointer<T*> { static const bool value = true; }; // 使用场景 cout << IsPointer<int>::value << endl; // 0(不是指针) cout << IsPointer<int*>::value << endl; // 1(是指针) cout << IsPointer<string*>::value << endl;// 1(是指针) 常见问题与避坑指南 编译/链接错误 错误类型 原因 解决方案 未定义引用 类模板方法放在 .cpp 文件中 将方法定义移至头文件,或包含 .cpp(不推荐) 二义性报错 多个偏特化版本匹配同一类型组合 新增全特化版本覆盖该类型组合,或调整偏特化条件 类型推断失败 创建对象时未指定类型参数 显式指定尖括号内的类型参数 逻辑错误 误以为静态成员是模板共享:不同实例的静态成员独立,需分别初始化 全特化后修改通用模板:全特化是独立类,通用模板修改不影响全特化版本 偏特化条件重叠:避免多个偏特化版本的条件交叉覆盖 学习提示与总结 核心价值:类模板是泛型编程的核心,实现“一次编写,多类型复用”,提升代码复用性和可维护性 重点掌握:模板定义、特化(偏特化+全特化)、成员方法实现位置、匹配优先级 实践建议: 优先使用类内实现简单方法,复杂方法类外实现(放在头文件) 避免偏特化条件重叠,必要时用全特化解决二义性 结合容器类(如 vector、list)理解模板的实际应用