当前位置: 首页 > news >正文

类模板 (Class Templates)


C++ 类模板 (Class Templates)

类模板允许定义通用的类,支持多种数据类型,是 静态多态(Static Polymorphism) 的核心机制之一。需要先了解函数模版, 再通过以下代码示例和底层原理,详细了解类模板的定义、实例化、特化及其高级用法。


1. 类模板基础

1.1 定义与语法

类模板通过 template 关键字声明,使用类型参数 T 表示泛型类型:

template <typename T>
class Box {
private:T content;
public:Box(T content) : content(content) {}T getContent() const { return content; }
};
  • typename T 表示类型参数(也可用 class T,二者等价)。
  • 模板参数可定义多个(如 template <typename T, int N>)。

1.2 实例化类模板

编译器根据使用的类型 隐式实例化 具体类:

int main() {Box<int> intBox(42);          // 隐式实例化为 Box<int>Box<string> strBox("Hello"); // 隐式实例化为 Box<string>cout << intBox.getContent() << endl; // 42cout << strBox.getContent() << endl; // Helloreturn 0;
}

1.3 底层原理
  • 编译器为每个类型生成独立类(如 Box<int>Box<string>)。
  • 符号表中生成唯一名称(Name Mangling),例如:
    • Box<int>_3BoxIiE
    • Box<string>_3BoxINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE
查看符号名(Linux/g++)
g++ -c main.cpp -o main.o
nm main.o | grep Box

输出示例:

00000000 W _ZN3BoxIiEC1Ei      # Box<int>::Box(int)
00000000 W _ZN3BoxIiE10getContentEv # Box<int>::getContent()

W: 弱符号(Weak Symbol),允许重复定义,链接时合并


2. 成员函数的外部定义

2.1 类内定义

成员函数直接在类内定义时,默认是内联的:

template <typename T>
class Box {
public:Box(T content) : content(content) {}T getContent() const { return content; } // 类内定义
private:T content;
};

2.2 类外定义

若成员函数在类外定义,需显式声明模板参数:

template <typename T>
class Box {
public:Box(T content);T getContent() const;
private:T content;
};// 构造函数外部定义
template <typename T>
Box<T>::Box(T content) : content(content) {}// 成员函数外部定义
template <typename T>
T Box<T>::getContent() const { return content; }

3. 类模板特化

3.1 全特化 (Full Specialization)

为特定类型提供定制实现:

// 通用模板
template <typename T>
class Box {
public:string getType() const { return "Generic Box"; }
};// 全特化(针对 double)
template <>
class Box<double> {
public:string getType() const { return "Double Box"; }
};int main() {Box<int> intBox;    // 使用通用模板Box<double> dblBox; // 使用全特化版本cout << intBox.getType() << endl; // "Generic Box"cout << dblBox.getType() << endl; // "Double Box"return 0;
}

3.2 偏特化 (Partial Specialization)

对部分模板参数进行特化:

// 通用模板
template <typename T, typename U>
class Pair {
public:string getType() const { return "Generic Pair"; }
};// 偏特化(当两个类型相同时)
template <typename T>
class Pair<T, T> {
public:string getType() const { return "Same Type Pair"; }
};int main() {Pair<int, double> p1; // 通用模板Pair<int, int> p2;    // 偏特化版本cout << p1.getType() << endl; // "Generic Pair"cout << p2.getType() << endl; // "Same Type Pair"return 0;
}

4. 静态成员与类模板

静态成员在每个实例化的类中独立存在:

template <typename T>
class Counter {
public:static int count;Counter() { count++; }
};// 初始化静态成员
template <typename T>
int Counter<T>::count = 0;int main() {Counter<int> a, b;Counter<double> c;cout << Counter<int>::count << endl;   // 2cout << Counter<double>::count << endl; // 1return 0;
}

5. 高级主题

5.1 友元函数与类模板

在类模板中声明友元函数:

template <typename T>
class Box {
private:T content;
public:Box(T content) : content(content) {}// 声明友元函数(需为模板)friend ostream& operator<<(ostream& os, const Box<T>& box) {os << box.content;return os;}
};int main() {Box<int> box(42);cout << box << endl; // 42return 0;
}

5.2 变长模板参数(C++11)

支持任意数量的模板参数:

template <typename... Args>
class Tuple {};int main() {Tuple<int, double, string> t;return 0;
}

5.3 类型别名(C++11 using)

简化模板类名:

template <typename T>
using Vec = std::vector<T>;int main() {Vec<int> numbers = {1, 2, 3};return 0;
}

6. 两阶段编译与错误检查

6.1 阶段一(模板定义阶段)
  • 检查语法和非依赖名称。
  • 例如,模板中的语法错误(如 int x = "error";)直接报错。
6.2 阶段二(模板实例化阶段)
  • 生成具体类代码,检查依赖名称。
  • 例如,调用未定义的成员函数 T::foo(),仅在实例化时报告错误。

7. 显式实例化控制

减少重复编译开销:

// 显式实例化声明(头文件)
extern template class Box<int>;// 显式实例化定义(源文件)
template class Box<int>;

8. 总结

8.1 核心优势
  • 代码复用:同一逻辑适用于不同类型。
  • 类型安全:避免 void* 等不安全操作。
  • 性能:编译时生成代码,无运行时开销。
8.2 适用场景
  • 容器类(如 std::vector<T>)。
  • 数学库(如矩阵运算)。
  • 工厂模式(创建泛型对象)。
8.3 注意事项
  • 代码膨胀:过多实例化增加二进制体积。
  • 编译时间:复杂模板增加编译时间。
  • 错误信息:模板错误信息可能难以理解。

多选题


题目 1:类模板的成员函数惰性实例化

以下代码是否能编译通过?为什么?

template <typename T>
class DataProcessor {
public:void processA(T value) { value.foo(); } // T 需要 foo()void processB(T value) { value.bar(); } // T 需要 bar()
};struct ValidType {void foo() {}void bar() {}
};struct PartialType {void foo() {}// 缺少 bar()
};int main() {DataProcessor<ValidType> dp1;dp1.processA(ValidType{}); // 调用 processADataProcessor<PartialType> dp2;// dp2.processB(PartialType{}); // 若取消注释,结果如何?return 0;
}

A. 编译成功,所有成员函数默认生成
B. 编译失败,DataProcessor<PartialType> 未定义 processB
C. 编译成功,但取消注释后因 PartialType 缺少 bar() 报错
D. 编译失败,模板类必须完全合法


题目 2:全特化与偏特化的优先级

以下代码的输出是什么?

template <typename T>
class Adapter {
public:void describe() { cout << "Generic Adapter" << endl; }
};template <typename T>
class Adapter<T*> {
public:void describe() { cout << "Pointer Adapter" << endl; }
};template <>
class Adapter<int*> {
public:void describe() { cout << "Int Pointer Adapter" << endl; }
};int main() {Adapter<float*> p1;Adapter<int*> p2;p1.describe();p2.describe();return 0;
}

A. Pointer AdapterInt Pointer Adapter
B. Generic AdapterInt Pointer Adapter
C. Pointer AdapterGeneric Adapter
D. 编译失败,存在歧义


题目 3:静态成员与模板实例化

以下代码的输出是什么?

template <typename T>
class Tracker {
public:static int count;Tracker() { count++; }~Tracker() { count--; }
};template <typename T>
int Tracker<T>::count = 0;int main() {Tracker<int> t1, t2;{Tracker<double> t3;cout << Tracker<int>::count << " " << Tracker<double>::count << " ";}cout << Tracker<int>::count << endl;return 0;
}

A. 2 1 2
B. 2 1 1
C. 2 1 0
D. 2 0 2


题目 4:友元函数与模板参数关联

以下代码是否能编译通过?为什么?

template <typename T>
class Wrapper {
private:T data;
public:Wrapper(T d) : data(d) {}// 友元声明friend void debugPrint(Wrapper<T> w);
};template <typename T>
void debugPrint(Wrapper<T> w) {cout << "Data: " << w.data << endl;
}int main() {Wrapper<int> w(42);debugPrint(w);return 0;
}

A. 编译成功,输出 Data: 42
B. 编译失败,debugPrint 未声明为模板函数
C. 编译失败,友元函数无法访问 data
D. 链接失败,debugPrint 未定义


题目 5:模板类与动态多态的交互

以下代码的输出是什么?

template <typename T>
class Base {
public:virtual void print() { cout << "Base<T>" << endl; }
};template <>
class Base<int> {
public:virtual void print() { cout << "Base<int>" << endl; }
};class Derived : public Base<int> {
public:void print() override { cout << "Derived" << endl; }
};int main() {Base<int>* b = new Derived();b->print();delete b;return 0;
}

A. Base<T>
B. Base<int>
C. Derived
D. 编译失败,模板类不能全特化



答案与解析


题目 1:类模板的成员函数惰性实例化

答案:C
解析

  • 类模板的成员函数仅在调用时实例化(惰性实例化)。
  • DataProcessor<ValidType>processA 被调用,合法。
  • DataProcessor<PartialType>processB 未被调用,因此即使 PartialType 缺少 bar(),只要不调用 processB,代码仍可编译。若取消注释,实例化 processB 时会因 bar() 缺失报错。

题目 2:全特化与偏特化的优先级

答案:A
解析

  • 优先级规则:全特化 > 偏特化 > 通用模板。
  • Adapter<float*> 匹配偏特化 T*,输出 Pointer Adapter
  • Adapter<int*> 匹配全特化,输出 Int Pointer Adapter

题目 3:静态成员与模板实例化

答案:B
解析

  • 静态成员在每个模板实例中独立存在。
  • Tracker<int> 实例化两次(t1, t2),count 为 2。
  • Tracker<double> 实例化一次(t3),离开作用域后析构,count 变为 0。
  • 输出:2 1 2(内部作用域)→ 离开作用域后 Tracker<int>::count 仍为 2?
    更正:题目设计有误,正确输出应为 2 1 2(选项 A),但析构后 count--,最终 Tracker<int>::count 应为 2(两次构造,两次析构)。实际答案为 A。

题目 4:友元函数与模板参数关联

答案:D
解析

  • 友元函数 debugPrint 声明为非模板函数,但实际定义是模板函数,导致链接时找不到非模板版本的 debugPrint
  • 正确做法:在类内声明友元函数为模板:
    template <typename U>
    friend void debugPrint(Wrapper<U> w);
    

题目 5:模板类与动态多态的交互

答案:C
解析

  • Base<int> 是全特化版本,Derived 继承并重写 print()
  • 通过基类指针调用虚函数,触发动态绑定,输出 Derived

总结

这些题目覆盖了类模板的惰性实例化、特化优先级、静态成员、友元函数和继承多态等高级主题。通过分析这些场景,可以深入理解模板的静态多态机制及其与动态多态的交互,从而编写更健壮的泛型代码。

相关文章:

  • 负环-P3385-P2136
  • 【Microsoft Store 中的软件推荐】
  • 4月24号
  • 乡村治理数字化平台:信息技术赋能乡村振兴的深度探索
  • 西甲001:奥萨苏纳VS塞维利亚
  • 手机访问电脑端Nginx服务器配置方式
  • 【力扣刷题|第五天作业】二分查找-寻找旋转排序数组中的最小值 II
  • frome time import * 与 import time
  • 多物理场仿真底层算法到AI智能仿真革命
  • Win11右键显示全部内容
  • 架构-软件架构设计
  • Windows下QT打包后程序运行后弹出CMD命令窗口的问题解决方法
  • 某大型电解铝厂电解系统谐波治理装置改造沃伦森电气
  • 深度学习3.7 softmax回归的简洁实现
  • 基于大模型的食管平滑肌瘤全周期预测与诊疗方案研究
  • Kaamel白皮书:Model Context Protocol (MCP) 隐私安全最佳实践
  • 沁恒CHV203中断嵌套导致修改线程栈-韦东山
  • 什么是IT人力外包?IT人力外包服务流程分为哪些步骤?
  • 序论文42 | patch+MLP用于长序列预测
  • Python基础语法:标识符,运算符,数据输入input(),数据输出print(),转义字符,续行符
  • 杨荫凯已任浙江省委常委、组织部部长
  • 商务部就海外社交平台上发布从所谓中国“代工厂”低价购买国际知名品牌事答问
  • 双拥主题歌曲MV:爱我人民,爱我军
  • 宁德时代与广汽等五车企发布10款巧克力换电新车型:年内将将完成30城1000站计划
  • 生态环境部:我国核电规模全球第一,总体安全可控
  • 安徽临泉一小区交付后多楼层现裂缝,专家组论证称不影响安全