C++学习24、模板

C++是一种功能强大的编程语言,它支持面向对象编程和泛型编程。泛型编程的核心概念之一就是模板(Template)。模板允许程序员编写可以在多种数据类型上工作的通用代码,从而提高代码的复用性和可维护性。本文将详细介绍C++中的模板概念,包括函数模板和类模板,并通过实例代码帮助读者更好地理解模板的使用。

一、模板的基本概念

1.什么是模板?

模板是C++中的一种机制,允许程序员编写可以在多种数据类型上工作的代码。通过使用模板,程序员可以定义函数或类,而无需指定具体的类型。编译器会在编译时根据实际使用的数据类型生成相应的代码。

2.为什么使用模板?

  • 代码复用:模板允许编写通用的代码,避免重复编写类似的代码。
  • 类型安全:模板在编译时进行类型检查,确保类型安全。
  • 性能优化:模板在编译时生成具体的代码,避免了运行时的类型转换开销。

二、函数模板

1.定义函数模板

函数模板的定义类似于普通函数,但在函数名前加上template关键字,并指定模板参数列表。模板参数列表通常使用尖括号<>括起来,可以包含一个或多个模板参数。
示例:简单的函数模板

#include <iostream>

// 定义一个函数模板,用于交换两个变量的值
template <typename T>
void swap(T &a, T &b) {
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5, y = 10;
    std::cout << "Before swap: x = " << x << ", y = " << y << std::endl;
    swap(x, y);
    std::cout << "After swap: x = " << x << ", y = " << y << std::endl;

    double a = 3.14, b = 2.71;
    std::cout << "Before swap: a = " << a << ", b = " << b << std::endl;
    swap(a, b);
    std::cout << "After swap: a = " << a << ", b = " << b << std::endl;

    return 0;
}

在这个示例中,swap函数模板可以用于交换任意类型的两个变量的值。编译器会根据实际使用的数据类型生成相应的代码。

2.多个模板参数

函数模板可以有多个模板参数,每个模板参数之间用逗号分隔。
示例:带有多个模板参数的函数模板

#include <iostream>

// 定义一个函数模板,用于比较两个变量的大小
template <typename T1, typename T2>
bool compare(T1 a, T2 b) {
    return a < b;
}

int main() {
    int x = 5;
    double y = 3.14;
    std::cout << "x < y: " << compare(x, y) << std::endl;

    char c = 'A';
    std::string s = "Hello";
    std::cout << "c < s: " << compare(c, s) << std::endl;

    return 0;
}

在这个示例中,compare函数模板可以比较不同类型的两个变量。编译器会根据实际使用的数据类型生成相应的代码。

3.默认模板参数

从C++11开始,函数模板可以有默认模板参数。
示例:带有默认模板参数的函数模板

#include <iostream>

// 定义一个函数模板,带有默认模板参数
template <typename T = int>
void print(T value) {
    std::cout << value << std::endl;
}

int main() {
    print(5);       // 使用默认模板参数 int
    print(3.14);    // 自动推导模板参数为 double
    print("Hello"); // 自动推导模板参数为 const char*

    return 0;
}

在这个示例中,print函数模板有一个默认模板参数int。如果没有显式指定模板参数,编译器会自动推导模板参数。

4.特化函数模板

有时,我们需要为特定的数据类型提供不同的实现。这时可以使用函数模板特化。
示例:函数模板特化

#include <iostream>
#include <string>

// 定义一个通用的函数模板
template <typename T>
void print(T value) {
    std::cout << "Generic template: " << value << std::endl;
}

// 定义一个特化的函数模板,用于处理 std::string 类型
template <>
void print<std::string>(std::string value) {
    std::cout << "Specialized template for std::string: " << value << std::endl;
}

int main() {
    print(5);          // 使用通用模板
    print(3.14);       // 使用通用模板
    print("Hello");    // 使用通用模板
    print(std::string("World")); // 使用特化模板

    return 0;
}

在这个示例中,print函数模板有一个通用的实现和一个特化的实现,用于处理std::string类型。

三、类模板

1.定义类模板

类模板的定义类似于普通类,但在类名前加上template关键字,并指定模板参数列表。
示例:简单的类模板

#include <iostream>

// 定义一个类模板,用于表示一个简单的栈
template <typename T>
class Stack {
private:
    T* data;
    size_t capacity;
    size_t top;

public:
    Stack(size_t cap) : capacity(cap), top(0) {
        data = new T[capacity];
    }

    ~Stack() {
        delete[] data;
    }

    void push(const T& value) {
        if (top >= capacity) {
            std::cerr << "Stack overflow!" << std::endl;
            return;
        }
        data[top++] = value;
    }

    T pop() {
        if (top == 0) {
            std::cerr << "Stack underflow!" << std::endl;
            return T();
        }
        return data[--top];
    }

    bool isEmpty() const {
        return top == 0;
    }
};

int main() {
    Stack<int> intStack(5);
    intStack.push(1);
    intStack.push(2);
    intStack.push(3);

    while (!intStack.isEmpty()) {
        std::cout << intStack.pop() << std::endl;
    }

    Stack<double> doubleStack(5);
    doubleStack.push(3.14);
    doubleStack.push(2.71);
    doubleStack.push(1.41);

    while (!doubleStack.isEmpty()) {
        std::cout << doubleStack.pop() << std::endl;
    }

    return 0;
}

在这个示例中,Stack类模板可以用于表示不同类型的栈。编译器会根据实际使用的数据类型生成相应的代码。

2.多个模板参数

类模板可以有多个模板参数,每个模板参数之间用逗号分隔。
示例:带有多个模板参数的类模板

#include <iostream>

// 定义一个类模板,用于表示一个简单的矩阵
template <typename T, size_t Rows, size_t Cols>
class Matrix {
private:
    T data[Rows][Cols];

public:
    void set(size_t row, size_t col, const T& value) {
        if (row >= Rows || col >= Cols) {
            std::cerr << "Index out of bounds!" << std::endl;
            return;
        }
        data[row][col] = value;
    }

    T get(size_t row, size_t col) const {
        if (row >= Rows || col >= Cols) {
            std::cerr << "Index out of bounds!" << std::endl;
            return T();
        }
        return data[row][col];
    }
};

int main() {
    Matrix<int, 2, 3> intMatrix;
    intMatrix.set(0, 0, 1);
    intMatrix.set(0, 1, 2);
    intMatrix.set(0, 2, 3);
    intMatrix.set(1, 0, 4);
    intMatrix.set(1, 1, 5);
    intMatrix.set(1, 2, 6);

    for (size_t i = 0; i < 2; ++i) {
        for (size_t j = 0; j < 3; ++j) {
            std::cout << intMatrix.get(i, j) << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

在这个示例中,Matrix类模板可以用于表示不同类型的矩阵,并且可以指定矩阵的行数和列数。编译器会根据实际使用的数据类型和维度生成相应的代码。

3.默认模板参数

从C++11开始,类模板可以有默认模板参数。
示例:带有默认模板参数的类模板

#include <iostream>

// 定义一个类模板,带有默认模板参数
template <typename T = int, size_t Size = 10>
class Array {
private:
    T data[Size];

public:
    void set(size_t index, const T& value) {
        if (index >= Size) {
            std::cerr << "Index out of bounds!" << std::endl;
            return;
        }
        data[index] = value;
    }

    T get(size_t index) const {
        if (index >= Size) {
            std::cerr << "Index out of bounds!" << std::endl;
            return T();
        }
        return data[index];
    }
};

int main() {
    Array<> defaultArray;
    defaultArray.set(0, 1);
    defaultArray.set(1, 2);
    defaultArray.set(2, 3);

    for (size_t i = 0; i < 10; ++i) {
        std::cout << defaultArray.get(i) << " ";
    }
    std::cout << std::endl;

    Array<double, 5> doubleArray;
    doubleArray.set(0, 3.14);
    doubleArray.set(1, 2.71);
    doubleArray.set(2, 1.41);

    for (size_t i = 0; i < 5; ++i) {
        std::cout << doubleArray.get(i) << " ";
    }
    std::cout << std::endl;

    return 0;
}

在这个示例中,Array类模板有一个默认模板参数int和10。如果没有显式指定模板参数,编译器会使用默认模板参数。

4.特化类模板

有时,我们需要为特定的数据类型提供不同的实现。这时可以使用类模板特化。
示例:类模板特化

#include <iostream>
#include <string>

// 定义一个通用的类模板
template <typename T>
class Container {
public:
    void add(const T& value) {
        std::cout << "Adding generic type: " << value << std::endl;
    }
};

// 定义一个特化的类模板,用于处理 std::string 类型
template <>
class Container<std::string> {
public:
    void add(const std::string& value) {
        std::cout << "Adding specialized type: " << value << std::endl;
    }
};

int main() {
    Container<int> intContainer;
    intContainer.add(5);

    Container<double> doubleContainer;
    doubleContainer.add(3.14);

    Container<std::string> stringContainer;
    stringContainer.add("Hello");

    return 0;
}

在这个示例中,Container类模板有一个通用的实现和一个特化的实现,用于处理std::string类型。

5.成员函数模板

类模板的成员函数也可以是模板。
示例:成员函数模板

#include <iostream>

// 定义一个类模板
template <typename T>
class Box {
private:
    T content;

public:
    Box(const T& value) : content(value) {}

    // 成员函数模板
    template <typename U>
    void print(U prefix) const {
        std::cout << prefix << ": " << content << std::endl;
    }
};

int main() {
    Box<int> intBox(5);
    intBox.print("Integer content");

    Box<double> doubleBox(3.14);
    doubleBox.print("Double content");

    Box<std::string> stringBox("Hello");
    stringBox.print("String content");

    return 0;
}

在这个示例中,Box类模板有一个成员函数模板print,它可以接受不同类型的前缀参数。

四、模板的高级特性

1.可变参数模板

从C++11开始,模板支持可变参数模板,允许函数模板和类模板接受任意数量的参数。
示例:可变参数模板

#include <iostream>

// 基本情况
void print() {
    std::cout << std::endl;
}

// 递归情况
template <typename T, typename... Args>
void print(T first, Args... args) {
    std::cout << first << " ";
    print(args...);
}

int main() {
    print(1, 2.5, "Hello", 'A');

    return 0;
}

在这个示例中,print函数模板可以接受任意数量的参数,并逐个打印出来。

2.模板别名

从C++11开始,可以使用using关键字定义模板别名。
示例:模板别名

#include <iostream>
#include <vector>

// 定义一个模板别名
template <typename T>
using Vec = std::vector<T>;

int main() {
    Vec<int> intVec = {1, 2, 3, 4, 5};
    for (int num : intVec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    Vec<std::string> stringVec = {"Hello", "World"};
    for (const std::string& str : stringVec) {
        std::cout << str << " ";
    }
    std::cout << std::endl;

    return 0;
}

在这个示例中,Vec是一个模板别名,用于简化std::vector的使用。

3.模板元编程

模板元编程是指在编译时使用模板进行计算和生成代码的技术。模板元编程可以用于实现复杂的算法和数据结构。
示例:模板元编程

#include <iostream>

// 定义一个模板元函数,用于计算阶乘
template <int N>
struct Factorial {
    static const int value = N * Factorial<N - 1>::value;
};

// 基本情况
template <>
struct Factorial<0> {
    static const int value = 1;
};

int main() {
    std::cout << "Factorial of 5: " << Factorial<5>::value << std::endl;
    std::cout << "Factorial of 10: " << Factorial<10>::value << std::endl;

    return 0;
}

在这个示例中,Factorial是一个模板元函数,用于计算阶乘。编译器在编译时会生成相应的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值