南通企业网站排名,安全邮箱注册网站,网站常用的颜色,网页界面设计罗军引言
泛型编程#xff08;Generic Programming#xff09;是一种编程范式#xff0c;允许编写与类型无关的代码#xff0c;从而使程序更加灵活和可重用。在C中#xff0c;泛型编程主要通过模板#xff08;Templates#xff09;来实现。模板使得我们可以编写通用…引言
泛型编程Generic Programming是一种编程范式允许编写与类型无关的代码从而使程序更加灵活和可重用。在C中泛型编程主要通过模板Templates来实现。模板使得我们可以编写通用函数和类从而在不同类型之间复用相同的算法或逻辑。这样程序的灵活性和可扩展性得到了极大的提升。
例如一个交换函数我们可能由于不同的参数类型要重载多个函数。
void Swap(int left, int right)
{
int temp left;
left right;
right temp;
}
void Swap(double left, double right)
{
double temp left;
left right;
right temp;
}
void Swap(char left, char right)
{
char temp left;
left right;
right temp;
}
而c的模版便是告诉编译器一个模子让编译器根据不同的类型通过该模子来生成代码只需要下面一段代码就能实现上面的所有功能
templatetypename T
T Add(const T x, const T y)
{T tmp x;x y;y tmp;
}
接下来让我们一起来深入了解 C 模板的概念、用法以及一些高级特性吧。
1.函数模版
函数模板允许我们创建一个可以处理不同类型参数的函数。
1.1基本语法
template typename T
T functionName(T arg1, T arg2) {// 函数体
}template typename T定义一个模版T是类型参数类型参数可自定义
T functionName (T arg1, T arg2)函数返回类型和参数类型均为模板参数
1.2实例
隐式化实例
#include iostream
using namespace std;
templatetypename T
T Add(const T x, const T y)
{return x y;
}int main()
{int a1 10;int a2 20;double b1 10.1;double b2 20.1;Add(a1, a2);Add(b1, b2);//没有与参数列表匹配的函数模版实例//Add(a1, b1);// 此时有两种处理方式1. 用户自己来强制转化 2. 使用显式实例化Add(a1, (int)b1);return 0;
}
显示化实例在函数名后的中指定模版参数的实际类型
int main()
{int a1 10;double b1 10.1;Addint(a1, b1);return 0;
}
1.3模版参数匹配原则
完全匹配优先非模板函数优先
当调用模板时编译器首先尝试找到与调用模版参数类型完全匹配的模版实例如果非模板函数参数完全匹配则优先选择非模板函数
// 专门处理int的加法函数
int Add(int left, int right)
{return left right;
}
// 通用加法函数
templateclass T1, class T2
T1 Add(T1 left, T2 right)
{return left right;
}
void Test()
{Add(1, 2); // 与非函数模板类型完全匹配不需要函数模板实例化Add(1, 2.0); // 模板函数可以生成更加匹配的版本编译器根据实参生成更加匹配的Add函数
}
1.4函数模板默认参数
函数模板的默认参数是在函数模板定义中为模板参数指定的默认值。当使用函数模板时如果没有为相应的模板参数提供具体的值就会使用默认参数。 以下是一个函数模板默认参数的示例
#include iostream
using namespace std;templatetypename T int, int N 10
void printArray(T arr[]) {for (int i 0; i N; i) {cout arr[i] ;}cout std::endl;
}int main() {int intArray[] {1, 2, 3, 4, 5};printArray(intArray); // 使用默认参数T 为 intN 为 10double doubleArray[] {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7};printArraydouble, 7(doubleArray); // 显式指定模板参数T 为 doubleN 为 7return 0;
} 在这个例子中函数模板 printArray 有两个模板参数 T 和 N 分别表示数组元素的类型和数组的大小。 T 的默认类型是 int N 的默认值是 10 。 需要注意的是函数模板默认参数的指定应该遵循一定的规则 1. 默认参数只能从右向左依次提供即如果某个模板参数有默认参数那么它右边的所有模板参数也必须有默认参数。 2. 在函数模板调用时如果要为某个模板参数提供具体的值那么它左边的所有模板参数也必须显式地指定。
2.类模版
类模板允许我们创建可以处理任意数据类型的类。
2.1类模板的基本语法
类模板的语法类似于函数模板。我们通过template关键字引入类型参数或其他参数以定义一个类模板。常见的形式是
templateclass T1, class T2, ..., class Tn
class 类模板名
{
// 类内成员定义
};
这里的T是一个模板参数表示将来可以用来替代任何类型。Class类中的成员变量、构造函数和成员函数都使用模板参数T因此该类可以处理不同类型的数据。
2.2实例
// 类模版
templatetypename T
class Stack
{
public:Stack(size_t capacity 4){_array new T[capacity];_capacity capacity;_size 0;}
void Push(const T data);
private:T* _array;size_t _capacity;size_t _size;
};
2.3非类型模板参数
除了类型参数外类模板还可以接受非类型的模板参数。非类型参数通常用于定义与类型无关的值例如数组的大小、常量等。例子如下
templatetypename T, int Size
class Array {
private:T arr[Size];
public:Array() {for (int i 0; i Size; i) {arr[i] 0;}}T operator[](int index) {return arr[index];}int getSize() const {return Size;}
};
在这里Array类模板接受一个类型参数T和一个整数参数Size用于定义数组的大小。该模板可以实例化不同类型和大小的数组。
使用方式
int main() {Arrayint, 5 intArray; // 定义一个包含 5 个 int 元素的数组intArray[0] 10;std::cout intArray[0] std::endl; // 输出 10std::cout Size: intArray.getSize() std::endl; // 输出 5
}
2.4. 类模板的默认参数
类模板和函数模板一样也可以为模板参数提供默认值。
templatetypename T int
class MyClass {
private:T value;
public:MyClass(T v) : value(v) {}T getValue() const { return value; }
};int main() {MyClass obj(10); // 使用默认的 int 类型std::cout obj.getValue() std::endl; // 输出 10
}
在这里如果没有提供模板参数MyClass会默认使用int类型
需要注意的是类模板默认参数的指定也要遵循一定的规则 1. 默认参数只能从右向左依次提供即如果某个模板参数有默认参数那么它右边的所有模板参数也必须有默认参数。 2. 在函数模板调用时如果要为某个模板参数提供具体的值那么它左边的所有模板参数也必须显式地指定。
3.模板的特化
模板特化允许我们为特定类型定义不同于通用模板的特殊实现。当通用的模板定义不能满足某些特定类型的需求时可以通过模板特化来提供专门的实现。类模板的特化分为完全特化和部分特化。
- 例如假设有一个通用的模板函数用于比较两个值的大小
templatetypename T
bool compare(T a, T b) {return a b;
}
这个函数可以比较任何类型的值。但是如果对于特定类型如指针类型需要不同的比较方式可以进行模板特化
template
bool compareint*(int* a, int* b) {return *a *b;
} 在这个特化版本中专门针对 int* 类型的指针进行了比较比较的是指针所指向的值的大小而不是指针本身的地址大小。
3.1模板的完全特化
函数模板的完全特化
函数模板的特化可以为某种具体类型提供定制的实现
#include iostreamtemplatetypename T
void func(T value) {std::cout General template: value std::endl;
}// 完全特化针对 char* 类型
template
void funcchar*(char* value) {std::cout Specialized for char*: value std::endl;
}int main() {func(10); // 调用通用模板输出 General template: 10func(Hello); // 调用特化版本输出 Specialized for char*: Hello
} 在这个例子中函数模板被特化为char*类型并为该类型提供了不同的实现。
类模板的完全特化
类模板的完全特化是针对某一特定类型提供特殊的实现。例如我们为bool类型特化一个类模板
templatetypename T
class MyClass {
public:void print() {std::cout General template\n;}
};// 针对 bool 类型进行完全特化
template
class MyClassbool {
public:void print() {std::cout Specialized for bool\n;}
};int main() {MyClassint obj1;obj1.print(); // 输出 General templateMyClassbool obj2;obj2.print(); // 输出 Specialized for bool
}
在这个例子中当模板参数为bool时调用特化版本而其他类型调用通用模板。
3.2模板的部分特化
部分特化是指特化模板的某些参数而不是全部参数。类模板可以进行部分特化但函数模板不能进行部分特化。
1类模板的部分特化
#include iostream// 通用模板
templatetypename T, typename U
class MyClass {
public:void display() {std::cout General template\n;}
};// 部分特化当第二个参数是 int 时
templatetypename T
class MyClassT, int {
public:void display() {std::cout Partially specialized template for second parameter int\n;}
};int main() {MyClassdouble, double obj1;obj1.display(); // 输出 General templateMyClassdouble, int obj2;obj2.display(); // 输出 Partially specialized template for second parameter int
} 在这个例子中当第二个模板参数为int时会使用特化的模板。其他类型组合仍然使用通用模板。
2指针和引用的部分特化
部分特化还可以用于特定的类型模式例如指针或引用类型。如下例
#include iostream// 通用模板
templatetypename T
class MyClass {
public:void display() {std::cout General template\n;}
};// 部分特化针对指针类型
templatetypename T
class MyClassT* {
public:void display() {std::cout Specialized template for pointer types\n;}
};int main() {MyClassint obj1;obj1.display(); // 输出 General templateMyClassint* obj2;obj2.display(); // 输出 Specialized template for pointer types
}
在这个例子中MyClassint*会匹配到特化的指针类型版本而MyClassint仍然使用通用模板。
3.3. 模板特化与继承
- 在 C中可以结合模板和继承来创建更加灵活和可扩展的类层次结构。通过模板参数可以在基类中定义一些通用的功能而派生类可以根据具体的需求进行特化或扩展。 - 例如考虑一个通用的容器类模板
templatetypename T
class Container {
public:void add(T item) {// 添加元素到容器的通用实现}// 其他通用的容器操作
};
然后可以创建一个派生类来特化这个容器类例如一个只存储整数的容器
class IntContainer : public Containerint {
public:// 可以添加针对整数容器的特殊操作
};
在这个例子中 IntContainer 继承自 Containerint 继承了通用容器类的功能并可以根据整数的特点添加特定的操作。 #include iostream
using namespace std;
templatetypename T
class Base {
public:void print() {cout Base template\n;}
};// 特化 Base 类针对 int 类型
template
class Baseint {
public:void print() {cout Specialized Base template for int\n;}
};templatetypename T
class Derived : public BaseT {
public:void show() {cout Derived template\n;this-print();}
};int main() {Deriveddouble obj1;obj1.show(); // 调用通用模板输出 Base templateDerivedint obj2;obj2.show(); // 调用特化模板输出 Specialized Base template for int
}
在这个例子中基类Baseint进行了完全特化当派生类继承自Baseint时它将使用特化版本的print()函数而其他类型使用通用版本。
4. 模板特化的应用场景
模板特化通常用于以下场景
- 处理某些类型的特殊需求例如对bool类型、指针类型、数组类型等进行特殊处理。 - 针对容器或算法进行优化在某些类型上进行优化以提高性能例如对于std::vectorbool的特殊优化。 - 处理特定类型的不同行为例如针对浮点数和整数提供不同的处理逻辑。
模板特化是C泛型编程中非常强大且灵活的特性。通过模板特化程序员可以为某些类型提供特定的处理方式而不影响其他类型的通用逻辑。理解和合理使用模板特化可以让代码更加高效、灵活。