C++模板

梦想不会自己发光,真正闪耀的是那个为梦狂奔的你。献给知行的孩子们!(Eric.He著)

  本教程将从模板的基本概念、语法规则到进阶应用,全面拆解 C++ 模板的核心用法,帮助你理解泛型编程的思想并灵活运用。

教程目录导航

一、C++ 模板的核心概述

1.1 基本定义

C++ 模板(Template)是泛型编程的核心技术,它允许我们定义 “类型无关” 的函数或类,将数据类型作为参数传递,从而实现代码的复用,避免为不同数据类型编写重复逻辑。

简单来说:模板就像 “代码模具”,只需定义一套逻辑,就能适配 int、float、自定义结构体等多种数据类型,无需重复编写相似代码。

模板示意图

1.2 模板的分类

C++ 模板主要分为两类,也是本教程的核心内容:

类型 作用
函数模板 用于创建类型无关的函数,适配不同类型的参数和返回值;
类、结构体模板 用于创建类型无关的类,适配类中的成员变量、成员函数参数及返回值类型。

1.3 模板的核心优势

二、函数模板(Function Template)

2.1 函数模板的定义语法

函数模板通过template关键字声明,语法格式如下:


// 模板参数列表:typename/class 关键字声明类型参数,可多个
template <typename T1, typename T2, ...>
返回值类型 函数名(参数列表) {
    // 函数逻辑(与类型无关)
}
        

示例:

需求:实现一个通用的交换函数,支持 int、float、字符串等多种类型的数据交换。


#include <iostream>
#include <cstring>
using namespace std;

// 定义函数模板:通用交换函数
template <typename T>
void mySwap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    // 1. 适配int类型
    int a = 10, b = 20;
    cout << "交换前:a=" << a << ", b=" << b << endl;
    mySwap(a, b);
    cout << "交换后:a=" << a << ", b=" << b << endl;

    // 2. 适配float类型
    float c = 3.14f, d = 6.28f;
    cout << "\n交换前:c=" << c << ", d=" << d << endl;
    mySwap(c, d);
    cout << "交换后:c=" << c << ", d=" << d << endl;

    // 3. 适配自定义结构体类型
    struct Student {
        char name[20];
        int age;
    };

    Student stu1 = {"张三", 18}, stu2 = {"李四", 19};
    cout << "\n交换前:stu1.name=" << stu1.name << ", stu2.name=" << stu2.name << endl;
    mySwap(stu1, stu2);
    cout << "交换后:stu1.name=" << stu1.name << ", stu2.name=" << stu2.name << endl;

    return 0;
}
        

运行结果:


交换前:a=10, b=20
交换后:a=20, b=10

交换前:c=3.14, d=6.28
交换后:c=6.28, d=3.14

交换前:stu1.name=张三, stu2.name=李四
交换后:stu1.name=李四, stu2.name=张三
        

2.2 函数模板的实例化

函数模板本身不生成可执行代码,编译期会根据实际调用时传入的参数类型,生成对应类型的具体函数,这个过程称为模板实例化

示例:显式实例化


int main() {
    int x = 5, y = 15;
    // 显式指定T为int类型
    mySwap<int>(x, y);
    cout << "显式实例化交换后:x=" << x << ", y=" << y << endl;

    double m = 2.5, n = 5.0;
    // 显式指定T为double类型
    mySwap<double>(m, n);
    cout << "显式实例化交换后:m=" << m << ", n=" << n << endl;

    return 0;
}
        

2.3 多参数函数模板

当函数需要适配多种不同类型时,可定义多个模板参数:


#include <iostream>
using namespace std;

// 多参数函数模板:T1为第一个参数类型,T2为第二个参数类型,T3为返回值类型
template <typename T1, typename T2, typename T3>
T3 add(T1 a, T2 b) {
    return static_cast<T3>(a + b);
}

int main() {
    // int + float = double
    int a = 10;
    float b = 3.14f;
    double res1 = add<int, float, double>(a, b);
    cout << "10 + 3.14 = " << res1 << endl;

    // long + int = long
    long c = 100000L;
    int d = 50000;
    long res2 = add<long, int, long>(c, d);
    cout << "100000 + 50000 = " << res2 << endl;

    return 0;
}
        

运行结果:


10 + 3.14 = 13.14
100000 + 50000 = 150000
        

2.4 函数模板的重载

函数模板可以与普通函数重载,也可以与其他函数模板重载,编译器会按优先级匹配:

  • 优先匹配普通函数;
  • 若普通函数不匹配,匹配最符合的函数模板;
  • 可通过显式实例化强制调用函数模板。
  • 示例:函数模板重载

    
    #include <iostream>
    using namespace std;
    
    // 函数模板:通用加法
    template <typename T>
    T add(T a, T b) {
        cout << "函数模板调用:";
        return a + b;
    }
    
    // 普通函数:int类型加法
    int add(int a, int b) {
        cout << "普通函数调用:";
        return a + b;
    }
    
    // 函数模板重载:多参数版本
    template <typename T1, typename T2>
    T1 add(T1 a, T2 b) {
        cout << "多参数函数模板调用:";
        return a + b;
    }
    
    int main() {
        // 匹配普通函数
        cout << add(10, 20) << endl;
    
        // 匹配单参数函数模板(隐式实例化)
        cout << add(3.14f, 6.28f) << endl;
    
        // 匹配多参数函数模板
        cout << add(10, 3.14f) << endl;
    
        // 强制调用单参数函数模板(显式实例化)
        cout << add<int>(10, 20) << endl;
    
        return 0;
    }
            

    运行结果:

    
    普通函数调用:30
    函数模板调用:9.42
    多参数函数模板调用:13.14
    函数模板调用:30
            

    三、结构体、类模板(Class Template)

    C++类模板适用结构体模板,使用方法相同。

    3.1 类模板的定义语法

    类模板用于创建类型无关的类,语法格式如下:

    
    // 模板参数列表
    template <typename T1, typename T2, ...>
    class 类名 {
    public:
        // 成员变量(可使用模板类型)
        T1 member1;
        T2 member2;
    
        // 成员函数(可使用模板类型)
        返回值类型 成员函数名(参数列表);
        // 成员函数内部实现
        void func() {
            // 逻辑实现
        }
    };
    
    // 类模板成员函数的外部实现(必须带模板参数列表和作用域解析符)
    template <typename T1, typename T2>
    返回值类型 类名<T1, T2<::成员函数名(参数列表) {
        // 逻辑实现
    }
    
    // 模板参数列表
    template <typename T1, typename T2, ...>
    struct 结构体名 {
        // 成员变量(可使用模板类型)
        T1 member1;
        T2 member2;
    
        // 成员函数(可使用模板类型)
        返回值类型 成员函数名(参数列表);
        // 成员函数内部实现
        void func() {
            // 逻辑实现
        }
    };
    
    // 结构体名模板成员函数的外部实现(必须带模板参数列表和作用域解析符)
    template <typename T1, typename T2>
    返回值类型 结构体名<T1, T2>::成员函数名(参数列表) {
        // 逻辑实现
    }

    示例:基础类模板示例

    需求:实现一个通用的栈(Stack)类,支持 int、float、自定义类型的数据存储。

    
    #include <iostream>
    #include <vector>
    using namespace std;
    
    // 定义类模板:通用栈
    template <typename T>
    class Stack {
    private:
        vector<T> data;  // 存储栈数据,T为模板类型
    public:
        // 入栈操作
        void push(const T& value) {
            data.push_back(value);
            cout << value << " 入栈成功" << endl;
        }
    
        // 出栈操作
        bool pop() {
            if (isEmpty()) {
                cout << "栈为空,无法出栈" << endl;
                return false;
            }
            cout << data.back() << " 出栈成功" << endl;
            data.pop_back();
            return true;
        }
    
        // 查看栈顶元素
        T top() const {
            if (isEmpty()) {
                throw "栈为空,无栈顶元素";  // 抛出异常
            }
            return data.back();
        }
    
        // 判断栈是否为空
        bool isEmpty() const {
            return data.empty();
        }
    
        // 获取栈的大小
        int size() const {
            return data.size();
        }
    };
    
    int main() {
        // 1. 实例化int类型的栈
        Stack<int> intStack;
        cout << "=== int类型栈操作 ===" << endl;
        intStack.push(10);
        intStack.push(20);
        intStack.push(30);
        cout << "栈顶元素:" << intStack.top() << endl;
        cout << "栈大小:" << intStack.size() << endl;
        intStack.pop();
        cout << "栈顶元素:" << intStack.top() << endl;
    
        // 2. 实例化string类型的栈
        Stack<string> strStack;
        cout << "\n=== string类型栈操作 ===" << endl;
        strStack.push("Hello");
        strStack.push("C++");
        strStack.push("Template");
        cout << "栈顶元素:" << strStack.top() << endl;
        strStack.pop();
        cout << "栈顶元素:" << strStack.top() << endl;
    
        // 3. 实例化自定义结构体类型的栈
        struct Student {
            string name;
            int age;
            // 重载 << 运算符,便于打印
            friend ostream& operator<<(ostream& os, const Student& stu) {
                os << "{" << stu.name << ", " << stu.age << "}";
                return os;
            }
        };
    
        Stack<Student> stuStack;
        cout << "\n=== Student类型栈操作 ===" << endl;
        stuStack.push({"张三", 18});
        stuStack.push({"李四", 19});
        cout << "栈顶元素:" << stuStack.top() << endl;
        stuStack.pop();
        cout << "栈顶元素:" << stuStack.top() << endl;
    
        return 0;
    }    
    

    运行结果:

    
    === int类型栈操作 ===
    10 入栈成功
    20 入栈成功
    30 入栈成功
    栈顶元素:30
    栈大小:3
    30 出栈成功
    栈顶元素:20
    
    === string类型栈操作 ===
    Hello 入栈成功
    C++ 入栈成功
    Template 入栈成功
    栈顶元素:Template
    Template 出栈成功
    栈顶元素:C++
    
    === Student类型栈操作 ===
    {张三, 18} 入栈成功
    {李四, 19} 入栈成功
    栈顶元素:{李四, 19}
    {李四, 19} 出栈成功
    栈顶元素:{张三, 18}
            

    3.2 类模板的实例化

    类模板必须显式实例化,即创建对象时必须手动指定模板参数类型,语法:类名<类型> 对象名;(不支持隐式推导)。

    
    int main() {
        // 1. 无参构造实例化
        Stack<float> floatStack;
        floatStack.push(3.14f);
        floatStack.push(6.28f);
    
        // 2. 带参构造实例化(若类有带参构造)
        // 假设Stack类有带参构造:Stack(int capacity) { ... }
        // Stack<double> doubleStack(10); // 初始化栈容量为10
    
        // 3. 指针形式实例化(堆区对象)
        Stack<string>* strStackPtr = new Stack<string>();
        strStackPtr->push("Test");
        cout << "堆区栈顶元素:" << strStackPtr->top() << endl;
        delete strStackPtr;  // 释放堆区内存
        strStackPtr = nullptr;
    
        return 0;
    }
    

    3.3 类模板的多参数与默认模板参数

    类模板支持多个模板参数,也可以为模板参数指定默认值(默认模板参数),简化实例化操作。

    
    #include <iostream>
    using namespace std;
    
    // 多参数类模板,指定默认模板参数:T2默认为int,T3默认为string
    template <typename T1, typename T2 = int, typename T3 = string>
    class MyData {
    public:
        T1 data1;
        T2 data2;
        T3 data3;
    
        // 构造函数
        MyData(T1 d1, T2 d2, T3 d3) : data1(d1), data2(d2), data3(d3) {}
    
        // 打印数据
        void printData() {
            cout << "data1: " << data1 << ", data2: " << data2 << ", data3: " << data3 << endl;
        }
    };
    
    int main() {
        // 1. 显式指定所有模板参数
        MyData<float, double, string> data1(3.14f, 6.28, "测试1");
        cout << "显式指定所有参数:" << endl;
        data1.printData();
    
        // 2. 只指定第一个参数,使用默认的T2=int、T3=string
        MyData<char> data2('A', 100, "测试2");
        cout << "\n使用默认参数:" << endl;
        data2.printData();
    
        // 3. 指定前两个参数,使用默认的T3=string
        MyData<long, float> data3(100000L, 2.5f, "测试3");
        cout << "\n指定前两个参数:" << endl;
        data3.printData();
    
        return 0;
    }

    运行结果:

    
    显式指定所有参数:
    data1: 3.14, data2: 6.28, data3: 测试1
    
    使用默认参数:
    data1: A, data2: 100, data3: 测试2
    
    指定前两个参数:
    data1: 100000, data2: 2.5, data3: 测试3
            

    3.4 类模板的成员函数

    类模板的成员函数有两种实现方式:

    
    #include <iostream>
    #include <vector>
    using namespace std;
    
    // 类模板声明
    template <typename T>
    class Stack {
    private:
        vector<T> data;
    public:
        // 成员函数声明
        void push(const T& value);
        bool pop();
        T top() const;
        bool isEmpty() const;
        int size() const;
    };
    
    // 成员函数外部实现:必须带模板参数列表
    template <typename T>
    void Stack<T>::push(const T& value) {
        data.push_back(value);
        cout << value << " 入栈成功" << endl;
    }
    
    template <typename T>
    bool Stack<T>::pop() {
        if (isEmpty()) {
            cout << "栈为空,无法出栈" << endl;
            return false;
        }
        cout << data.back() << " 出栈成功" << endl;
        data.pop_back();
        return true;
    }
    
    template <typename T>
    T Stack<T>::top() const {
        if (isEmpty()) {
            throw "栈为空,无栈顶元素";
        }
        return data.back();
    }
    
    template <typename T>
    bool Stack<T>::isEmpty() const {
        return data.empty();
    }
    
    template <typename T>
    int Stack<T>::size() const {
        return data.size();
    }
    
    // 测试
    int main() {
        Stack<int> stack;
        stack.push(100);
        stack.push(200);
        cout << "栈顶元素:" << stack.top() << endl;
        stack.pop();
        return 0;
    }
        

    3.5 模板的分离编译问题

    类模板的声明和实现若分离在.h头文件和.cpp源文件中,会导致编译错误(编译器无法找到模板实例化的代码)。

    解决方案:

    四、模板的典型应用场景

    五、注意事项

    六、总结

    本教程将从模板的基本概念、语法规则到进阶应用,全面拆解 C++ 模板的核心用法,帮助你理解泛型编程的思想并灵活运用。掌握模板的运用,是实现可复用的数据结构重要基础之一。


    返回顶部