C++之类模板

在前面我们介绍了《C++之函数模板》

今天我们继续来介绍模板的另外一种形式:类模板。

与模板函数相似,类也可以被一种或多种类型参数化,标准库中的容器类就是一个具有这种特性的典型例子。

类模板的声明

我们通过一段例子代码了解一下类模板的声明:

arduino 复制代码
// 类模板声明
template <typename T>
class MyClass{
public:
    T getT();
    void setT(T t);
private:
    T t;
};
// 类成员函数实现
template<typename T>
T MyClass<T>::getT() {
    return t;
}

template<typename T>
void MyClass<T>::setT(T t) {
    this->t = t;
}

int main(int argc, char* argv[]) {
    // 类模板使用
    MyClass<int> myClass;
    myClass.setT(10);
    std::cout << "myClass:" << myClass.getT() << std::endl;
    return 0;
}

首先也是使用关键字template和关键字typename对类模板进行声明,当我们将一个类声明为类模板之后,模板参数T可以像其他任何类型一样,用于声明成员变量和成员函数。

类模板的成员函数

通过上面的实例代码,我们看到在类模板的声明内部函数时如果用到模板参数T,则不用再次使用template<typename T>,但是当实现类模板的内部函数时,如果用到了模板参数T,则还需要在函数的上方 使用template<typename T>进行标记。

也即是说为了定义类模板的成员函数,你必须指定该成员函数是一个函数模板,而且你还需要使用这个类模板的完整类型限定符。

或许你觉得每次定义类模板的内部函数都要使用到模板声明,当内部函数较多时,则会产生非常多的不必要的声明,此时我们可以直接在类的内部声明加定义同时实现:

arduino 复制代码
// 类模板声明
template <typename T>
class MyClass{
public:
    // 声明加定义
    T getT(){
        return t;
    }
    // 声明加定义
    void setT(T t){
        this->t = t;
    }
private:
    T t;
};

在类模板中只有那些被调用的成员函数,才会产生这些函数的实例化代码。对于类模板,成员函数只有在被使用的时候才会被实例化。 显然,这样可以节省空间和时间;另一个好处是:对于那些"未能提供所有成员函数中所有操作的"类型,你也可以使用该类型来实例化类模板, 只要对那些"未能提供某些操作的"成员函数,模板内部不使用即可。

类模板的特例化

同模板函数的特化一样,你可以用模板实参来特化类模板,和函数模板的重载类似,通过特化类模板,我们可以优化基于某种特定类型的实现。

在类模板的特化过程中有两个步骤:

  1. 在类的起始处声明一个template<>,接下来声明用来特化类模板的类型。这个类型被用作模板实参,且必须在类名的后面直接指定。

  2. 进行类模板的特化时,每个成员函数都必须重新定义为普通函数,原来模板函数中的每个T也相应地被进行特化的类型取代。

下面是一个模板类特化的例子:

c 复制代码
// 类模板声明
template <typename T>
class MyClass{
public:
    // 声明加定义
    T getT(){
        return t;
    }
    // 声明加定义
    void setT(T t){
        this->t = t;
    }
private:
    T t;
};

// 类模板声明
template <>
class MyClass<std::string>{
public:
    // 声明加定义
    std::string getT(){
        return t;
    }
    // 声明加定义
    void setT(std::string t){
        std::cout << "调用特化类模板 setT" << std::endl;
        this->t = t;
    }
private:
    std::string t;
};

int main(int argc, char* argv[]) {
    // 类模板使用
    MyClass<std::string> myClass;
    myClass.setT("hello word");
    std::cout << "myClass:" << myClass.getT() << std::endl;
    return 0;
}

模板源码组织模式

模板源码的组织模式有好多种,这里只介绍两种常用的:分别是包含模式和关键字export的分离模式。

包含模式可以说是最常用也是最推荐的一种模式。这种模式就是将模板类的声明和定义都放在同一个文件中,这个文件一般是扩展名为.hpp的文件。

下面是一个类模板声明定义和使用分开在不同文件的例子:

arduino 复制代码
MyClass.hpp
#include <iostream>
#include <memory>
// 类模板声明
template <typename T>
class MyClass{
public:
    // 声明
    T getT();

    void setT(T t);
private:
    T t;
};

template<typename T>
T MyClass<T>::getT() {
    return t;
}

template<typename T>
void MyClass<T>::setT(T t) {
    this->t = t;
}

在main函数中使用模板main.cpp:

c 复制代码
main.cpp

#include <iostream>
#include <memory>
#include "MyClass.hpp"
int main(int argc, char* argv[]) {
    // 类模板使用
    MyClass<std::string> myClass;
    myClass.setT("hello word");
    std::cout << "myClass:" << myClass.getT() << std::endl;
    return 0;
}

很明显,包含模式因为包含了类模板的定义实现,因而明显增加了包含头文件.hpp的开销,这会导致大大增加了编译复杂程序所耗费的时间。 然而随着现代的机器性能提升,这里带来的编译开销基本可以忽略不计,因此这种模式成为了使用最多的模式。

下面我们再来看看关键字export的分离模式。

关键字export的功能使用是非常简单的:在一个文件里面定义模板,并在模板的定义和(非定义的)声明的前面加上关键字export。

还是以上面的代码为例:

MyClass.h

arduino 复制代码
#include <iostream>
#include <memory>
// 类模板声明
export template <typename T>
class MyClass{
public:
    // 声明
    T getT();

    void setT(T t);
private:
    T t;
};

MyClass.cpp

arduino 复制代码
#include "MyClass.h"

export template<typename T>
T MyClass<T>::getT() {
    return t;
}

export template<typename T>
void MyClass<T>::setT(T t) {
    this->t = t;
}

以上代码能否编译通过取决于你的编译器,大部分是无法编译通过的,这代码和包含模式对比起来是不是有一种脱裤子放屁的感觉?

看起来关键字export的分离模式更加符合C++源码组织习惯,为什么这种写法再C++的模板没有流行起来呢? 这是因为在C++标准推出几年之后,也就只有极少的公司真正提供了对export关键字的支持。于是,export这个特性未能像其他C++特性那样广为流传,这就使得程序员在很多编译器下都不能正常使用export的分离模式。

相关推荐
AC赳赳老秦4 小时前
采集行为合规自检:OpenClaw 自动校验 robots 协议与采集频率,规避违规采集风险
java·开发语言·c++·python·php·deepseek·openclaw
无名猿4 小时前
C++ 查找算法:std::find 与容器成员 find 的性能差距
c++·性能优化·stl·标准库
程序猿编码5 小时前
工业视觉多任务方案:可编程梯度信息视觉框架,训练评估 + C++ 部署完整实战
开发语言·c++·视觉·推理引擎
朝朝辞暮i6 小时前
C++ 第 21 课:struct —— 把一组相关数据打包在一起
开发语言·c++·算法
SHARK_pssm7 小时前
【C++——类和对象(下)】
开发语言·c++·经验分享·笔记
汉克老师7 小时前
GESP2026年9月认证C++二级( 第一部分选择题(1~7题)精讲
c++·gesp·小学生·学c++编程
沫璃染墨8 小时前
《从零入门Linux系统篇(五十四):线程篇·七——互斥锁底层原理:从原子交换到线程竞争与锁实现》
linux·运维·服务器·开发语言·c++·驱动开发·系统架构
可乐ea9 小时前
GitHub Security Lab 开源 Fuzzing Taskflow:让 LLM 智能体自己跑完 C/C++ 模糊测试
jvm·c++·github·模糊测试·mcp·代码agent·智能体自动化
朝朝辞暮i9 小时前
C++ 第 13 课:值传递 —— 为什么函数里改了,外面却没变?
java·c++·算法
无忧.芙桃11 小时前
C++语言原理与实践(十):vector类的底层实现
开发语言·c++·算法