目录
如需咨询请添加个人微信: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 T
或 class 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;
}
在这个例子中,obj1
是 MyClass<int>
的一个实例,而 obj2
是 MyClass<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;
}