【C++基础】模板函数和模板类

目录

一、模板函数

1. 模板函数的基本概念

优点

2. 定义模板函数

语法

3. 模板函数的实例化

4. 模板参数

5. 模板函数的特化

6. 示例

二、模板类

1. 模板类的基本概念

优点

2. 定义模板类

语法

3. 模板类的实例化

示例

4. 非类型模板参数

示例

5. 模板类的特化

完全特化

偏特化

6. 模板类的示例


如需咨询请添加个人微信:a15135158368

欢迎叨扰,多多交流


一、模板函数

1. 模板函数的基本概念

模板函数是一个可以接受任意数据类型的函数模板

模板函数在编译时生成具体的函数实例,每个实例针对特定的数据类型。

这种特性允许函数在处理不同的数据类型时使用相同的函数体。

优点

  • 代码复用:模板函数可以处理多种数据类型,减少代码重复。

  • 类型安全:编译器在实例化时会检查类型匹配,减少了运行时错误。

  • 灵活性:模板函数支持各种类型和操作,使得函数更加通用。

2. 定义模板函数

模板函数的定义使用 template 关键字,并指定一个或多个模板参数。

模板参数通常是类型参数,但也可以是非类型参数。

语法

template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

在这个例子中,T 是模板参数,代表一个类型。

在函数调用时,编译器会根据实际传入的参数类型生成相应的函数实例。

3. 模板函数的实例化

当你调用模板函数时,编译器会根据你传入的参数类型实例化相应的函数。

int main() {
    int a = 5, b = 10;
    double x = 2.5, y = 3.7;
​
    cout << max(a, b) << endl;    // 调用 max<int>(int, int)
    cout << max(x, y) << endl;    // 调用 max<double>(double, double)
}

4. 模板参数

  • 类型模板参数:最常见的模板参数,代表数据类型 ,如 T

  • 非类型模板参数:可以是常量值,比如整数、指针等。例如:

    template <int N>
    void printN() {
        cout << N << endl;
    }
    ​
    int main() {
        printN<10>();  // 输出 10
    }
  • 模板模板参数:模板参数的类型是其他模板,表示接受其他模板作为参数。

    template <template <typename> class Container, typename T>
    void process(Container<T>& c) {
        // 处理容器中的元素
    }

5. 模板函数的特化

模板函数可以特化,即为特定类型提供不同的实现。这分为两种特化:

  • 完全特化:为某个特定类型提供专门的实现。

    template <>
    void printN<0>() {
        cout << "Zero" << endl;
    }
  • 偏特化:对模板参数的部分特化。仅对某些条件下的参数提供不同实现。

    template <typename T>
    class Array<T*> {
        // 专门处理指针类型的实现
    };

6. 示例

以下是一个简单的模板函数示例,用于交换两个变量的值:

template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}
​
int main() {
    int x = 1, y = 2;
    swap(x, y);  // 调用 swap<int>(int&, int&)
​
    double p = 1.1, q = 2.2;
    swap(p, q);  // 调用 swap<double>(double&, double&)
​
    return 0;
}

二、模板类

C++ 的模板类是一个能够根据类型参数生成不同类的模板。

与模板函数类似,模板类允许编写通用代码,可以处理不同的数据类型,进而提高代码的复用性和灵活性。

1. 模板类的基本概念

模板类是通过在类定义前使用 template 关键字引入模板参数,使得类可以接受一个或多个类型或常量参数。

模板类的实际类型在实例化时确定,这样一个模板类可以生成多个不同类型的类。

优点
  • 代码复用:模板类允许你编写一次代码,并用于处理多种不同的数据类型。

  • 类型安全:编译器在实例化模板类时,会确保类型安全,从而减少运行时错误。

  • 灵活性:通过模板参数,模板类能够适应不同的使用场景,而不需要重新编写代码。

2. 定义模板类

模板类的定义与模板函数类似,使用 template 关键字指定模板参数。

模板参数可以是类型参数(如 typename Tclass T),也可以是非类型参数(如 int N)。

语法
template <typename T>
class MyClass {
private:
    T data;
public:
    MyClass(T d) : data(d) {}
    
    T getData() const { return data; }
    
    void setData(T d) { data = d; }
};

在这个示例中,MyClass 是一个模板类,T 是一个类型模板参数。

类中的数据成员 data 的类型和成员函数的参数类型都是 T

3. 模板类的实例化

当你需要使用模板类时,必须为模板类指定具体的类型参数。这就是模板类的实例化。

示例
int main() {
    MyClass<int> obj1(10);      // 实例化 MyClass<int>
    MyClass<double> obj2(5.5);  // 实例化 MyClass<double>
​
    cout << obj1.getData() << endl;  // 输出 10
    cout << obj2.getData() << endl;  // 输出 5.5
​
    return 0;
}

在这个例子中,obj1MyClass<int> 的一个实例,而 obj2MyClass<double> 的一个实例。

4. 非类型模板参数

除了类型参数,模板类还可以接受非类型参数,例如整数、指针或引用。

非类型模板参数在类中可以作为常量使用。

示例
template <typename T, int size>
class Array {
private:
    T arr[size];
public:
    void set(int index, T value) {
        if (index >= 0 && index < size) {
            arr[index] = value;
        }
    }
    
    T get(int index) const {
        if (index >= 0 && index < size) {
            return arr[index];
        }
        return T(); // 返回默认值
    }
};
​
int main() {
    Array<int, 5> intArray;  // 实例化 Array<int, 5>
    intArray.set(0, 100);
    cout << intArray.get(0) << endl;  // 输出 100
​
    return 0;
}

在这个例子中,Array 类接受一个非类型参数 size,它在数组的大小定义中使用。

5. 模板类的特化

和模板函数一样,模板类也可以被特化。

特化意味着你可以为特定类型提供不同的实现。模板类的特化分为 完全特化偏特化

完全特化

完全特化是为某个特定的类型提供完全不同的实现。

template <>
class MyClass<char> {
private:
    char data;
public:
    MyClass(char d) : data(d) {}
    char getData() const { return data; }
    void setData(char d) { data = d; }
    void printAsInt() const { cout << static_cast<int>(data) << endl; }
};
​
int main() {
    MyClass<char> obj('A');
    obj.printAsInt();  // 输出 65(ASCII 码)
    return 0;
}

偏特化

偏特化是为模板参数的某些组合提供特化实现。

template <typename T>
class MyClass<T*> {
private:
    T* data;
public:
    MyClass(T* d) : data(d) {}
    T getData() const { return *data; }
    void setData(T d) { *data = d; }
};

在这个例子中,MyClass 对指针类型进行了偏特化。

6. 模板类的示例

下面是一个简单的数组类实现的模板类示例

实现了数组的构造、析构、深拷贝、赋值、元素访问、数组相加、数组追加、数组输出、元素添加、元素删除和排序等功能。

代码中的注释详细解释了每个部分的功能和实现细节。

/**
  ******************************************************************************
  * @file           : array_template.cpp
  * @author         : niuniu
  * @brief          : 数组类模板,构造析构,运算符号重载,[],  + ,+=, cout<<,   Arr  a;
                      功能:排序,删除,
  * @attention      : None
  * @date           : 2024/8/29
  ******************************************************************************
  */
#include <algorithm>
#include <iostream>
​
using namespace std;
​
// 模板类 Array,用于实现一个通用的数组类模板
template <typename T, int size>
class Array
{
private:
    T* arr;          // 动态数组指针,用于存储元素
    int current_size; // 当前数组的大小(元素个数)
​
public:
​
    // 默认构造函数
    Array() : current_size(0)
    {
        arr = new T[size];  // 为数组分配内存
        cout << "默认构造数组" << endl;
    }
​
    // 析构函数
    ~Array()
    {
        if(arr != nullptr)
        {
            delete []arr;  // 释放动态分配的内存
            cout << "析构数组" << endl;
        }
    }
​
    // 拷贝构造函数
    Array(const Array& array)
    {
        this->current_size = array.current_size;  // 拷贝当前数组大小
        this->arr = new T[size];  // 为新数组分配内存
        /*
         * copy函数参数说明:
         * 第一个参数:指向要复制的源范围的第一个元素的指针
         * 第二个参数:指向要复制范围的最后一个元素后位置的指针
         * 第三个参数:指向复制目标范围起始位置的指针
         * */
        copy(array.arr, array.arr + current_size, arr); // 复制数组元素
    }
​
    // 拷贝赋值运算符
    Array& operator=(const Array& array)
    {
        if (this == &array) return *this;  // 检查是否自我赋值
​
        delete []this->arr;  // 释放已有的内存
        this->arr = new T[size];  // 为新数组分配内存
        this->current_size = array.current_size;  // 拷贝当前数组大小
        copy(array.arr, array.arr + current_size , arr); // 复制数组元素
        return *this;
    }
​
    // 下标运算符重载
    T& operator[](int index)
    {
        if(index < 0 || index >= current_size)
        {
            cout << "下标索引越界 : " << index << endl;
            throw out_of_range("Index out of bounds");  // 抛出异常
        }
        return arr[index];  // 返回对应的数组元素
    }
​
    // "+"运算符重载:连接两个数组
    Array operator+(const Array& array)const
    {
        if((current_size + array.current_size) > size)
        {
            cout << "相加后数组大小大于定义的数组最大长度" << endl;
        }
        Array add_arr;  // 创建新数组
        add_arr.current_size = this->current_size + array.current_size;  // 新数组的大小
        add_arr.arr = new T[size + size];  // 为新数组分配更大的内存
        std::copy(arr, arr + current_size, add_arr.arr);  // 复制第一个数组的元素
        std::copy(array.arr, array.arr + array.current_size, add_arr.arr + current_size);  // 复制第二个数组的元素
​
        return add_arr;  // 返回新数组
    }
​
    // "+="运算符重载:追加数组
    Array& operator+=(const Array& array)
    {
        if((this->current_size + array.current_size) > size)
        {
            cout << "追加后的数组大小超过定义的数组大小" << endl;
        }
        std::copy(array.arr, array.arr + array.current_size, arr + current_size);  // 将要追加的数组元素复制到当前数组后面
        this->current_size += array.current_size;  // 更新当前数组大小
        return *this;
    }
​
    // "<<"运算符重载:打印数组
    friend ostream& operator<<(ostream &os, const Array& array)
    {
        os << '[';
        for(int i = 0; i < array.current_size; ++i)
        {
            os << array.arr[i];
            if (i < array.current_size - 1) os << ", ";  // 元素之间用逗号分隔
        }
        cout << ']' << endl;
        return os;
    }
​
    // 添加元素
    Array& add_element(const T& element)
    {
        if(current_size >= size)
        {
            cout << "当前的数组内存已满,无法添加 " << endl;
            return *this;
        }
        this->arr[current_size] = element;  // 将新元素添加到数组末尾
        current_size++;  // 更新数组大小
​
        return *this;
    }
​
    // 删除元素的辅助函数
    void delete_this(int i)
    {
        while(i < current_size - 1)
        {
            arr[i] = arr[i + 1];  // 将元素往前移动
            i++;
        }
    }
​
    // 删除指定的元素
    Array& delete_element(const T&element)
    {
        for(int i= 0; i < current_size; i++)
        {
            if(arr[i] == element)
            {
                delete_this(i);  // 找到元素后,删除该元素
                current_size--;  // 更新数组大小
                return *this;
            }
        }
        cout << "未找到要删除的值" << endl;
        return *this;
    }
​
    // 数组排序
    void sort()
    {
        std::sort(arr, arr + current_size);  // 使用标准库函数对数组进行排序
    }
​
    // 获取数组当前大小
    int get_size()const
    {
        return current_size;
    }
};
​
int main(void)
{
    Array<int , 10>a;  // 创建一个存储整数的数组,最大长度为10
    for (int i = 0; i < 5; ++i) {
        a.add_element(i + 1);  // 向数组中添加元素
    }
​
    cout << "Array a: " << a << endl;
​
    a.sort();  // 对数组进行排序
    cout << "Sorted a: " << a << endl;
​
    a.delete_element(3);  // 删除元素3
    cout << "a after removing 3: " << a << endl;
​
    Array<char, 10>b;  // 创建一个存储字符的数组,最大长度为10
    for (int i = 0; i < 3; ++i) {
        b.add_element(i + 65);  // 向数组中添加字符元素(ASCII码65为'A')
    }
​
    cout << "Array b: " << b << endl;
​
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值