函数模板
交换两个整型变量的值的Swap函数: void Swap(int & x,int & y) { int tmp = x; x = y; y = tmp; } 交换两个double型变量的值的Swap函数: void Swap(double & x,double & y) { double tmp = x; x = y; y = tmp; }
用函数模板解决:
用函数模板解决: template <class 类型参数1,class 类型参数2,……> 返回值类型 模板名 (形参表) { 函数体 }; template <class T> void Swap(T & x,T & y) { T tmp = x; x = y; y = tmp; } 函数模板 int main() { int n = 1,m = 2; Swap(n,m); //编译器自动生成 void Swap(int & ,int & )函数 double f = 1.2,g = 2.3; Swap(f,g); //编译器自动生成 void Swap(double & ,double & )函数 return 0; } void Swap(double & x,double & y) { double tmp = x; x = y; y = tmp
在C++中,函数模板是一种通用的函数定义,可以应用于不同的数据类型。它允许编写一次代码以适应多种不同的数据类型,实现代码的复用和泛化。
函数模板使用关键字 “template” 开始,并且后面跟着模板参数列表。模板参数列表可以包含一个或多个类型参数(如T、U等)或非类型参数(如整数常量)。例如:
template <typename T> T max(T a, T b) { return (a > b) ? a : b; }
上述代码中的函数模板 max
接受两个相同类型的参数,并返回较大的值。类型参数 T
可以是任何数据类型,比如整数、浮点数、字符等。
在实际调用函数模板时,编译器根据参数的类型将模板进行实例化,生成对应类型的函数。例如:
int result1 = max<int>(3, 5); // 实例化为 max<int>, 返回 5 double result2 = max<double>(2.7, 1.5); // 实例化为 max<double>, 返回 2.7 char result3 = max<char>('a', 'b'); // 实例化为 max<char>, 返回 'b'
在上面的例子中,通过 <类型>
的形式来指定实例化的具体类型,这样编译器就能够根据传入的类型生成对应的函数。如果没有显式指定类型,编译器会根据参数的类型自动推导出实例化的类型。
函数模板还可以有多个类型参数,并且可以有默认参数值。此外,你还可以在函数模板外定义非模板函数,它们可以与函数模板进行重载。
函数模板是C++中一种强大的工具,利用它可以编写通用且具有复用性的代码,可以处理不同类型的数据。
当需要在函数模板中处理多个不同类型的参数时,可以使用多个类型参数。
例如,下面是一个函数模板 swap
,用于交换两个值:
template <typename T> void swap(T& a, T& b) { T temp = a; a = b; b = temp; }
上述代码中的 swap
函数模板接受两个相同类型的引用参数,并交换它们的值。使用该函数模板时,编译器将根据实参的类型实例化对应的函数。
int x = 5, y = 10; swap(x, y); // 实例化为 swap<int>(x, y),交换 x 和 y 的值 double a = 2.5, b = 3.7; swap(a, b); // 实例化为 swap<double>(a, b),交换 a 和 b 的值
除了类型参数,函数模板还可以包含非类型参数。非类型参数可以是整数、枚举、指针或引用类型,但不能是浮点数、类类型或 void 类型。
下面是一个示例,演示如何在函数模板中使用非类型参数来指定数组的大小:
template <typename T, int size> void printArray(const T (&arr)[size]) { for (int i = 0; i < size; ++i) { cout << arr[i] << " "; } cout << endl; }
上述代码中的 printArray
函数模板接受一个固定大小的数组,并打印每个元素。通过将数组大小作为非类型参数传递给函数模板,可以在编译时知道数组的大小。
int intArray[] = {1, 2, 3, 4, 5}; printArray(intArray); // 实例化为 printArray<int, 5>(intArray) double doubleArray[] = {1.5, 2.7, 3.9}; printArray(doubleArray); // 实例化为 printArray<double, 3>(doubleArray)
这样,函数模板就可以根据不同的数组大小生成对应的函数。
需要注意的是,在函数模板的定义和声明中,通常将模板参数放在尖括号 < >
中,并使用关键字 typename
或 class
来声明类型参数。然而,你也可以使用非类型参数来调整模板的行为。
同时,函数模板还可以具有默认模板参数,以便更灵活地使用。默认模板参数允许指定某个或某些参数的默认值,使得在函数调用时可以省略掉这些参数。
C++中的函数模板是一种强大的工具,可以处理多个不同类型的参数,其中可以包含类型参数和非类型参数。通过使用函数模板,可以实现通用、可复用的代码,并根据实参的类型和值来自动生成对应的函数。
函数模板和函数的次序
在有多个函数和函数模板名字相同的情况下,编译器如下处理一条函数调用语句
- 先找参数完全匹配的普通函数(非由模板实例化而得的函数)。
- 再找参数完全匹配的模板函数。
- 再找实参数经过自动类型转换后能够匹配的普通函数。
- 上面的都找不到,则报错。
类模板
类模板 – 问题的提出
• 为了多快好省地定义出一批相似的类,可以定义类模板,然后由类模
板生成不同的类
• 数组是一种常见的数据类型,元素可以是:
– 整数
– 学生
– 字符串
– ……
• 考虑一个可变长数组类,需要提供的基本操作
– len():查看数组的长度
– getElement(int index):获取其中的一个元素
– setElement(int index):对其中的一个元素进行赋值
– ……
### 类模板的定义
template <typename 类型参数1,typename 类型参数2,……> //类型参数表 class 类模板名 { 成员函数和成员变量 }; 类模板里成员函数的写法: template <class 类型参数1,class 类型参数2,……> //类型参数表 返回值类型 类模板名<类型参数名列表>::成员函数名(参数表) { …… } 用类模板定义对象的写法: 类模板名 <真实类型参数表> 对象名(构造函数实参表); 类模板示例: Pair类模板 template <class T1,class T2> class Pair { public: T1 key; //关键字 T2 value; //值 Pair(T1 k,T2 v):key(k),value(v) { }; bool operator < ( const Pair<T1,T2> & p) const; }; template<class T1,class T2> bool Pair<T1,T2>::operator < ( const Pair<T1,T2> & p) const //Pair的成员函数 operator < { return key < p.key; }
类模板示例:Pair类模板
int main() { Pair<string,int> student("Tom",19); //实例化出一个类 Pair<string,int> cout << student.key << " " << student.value; return 0; } 输出: Tom 19
类模板(Class Template)是C++中另一种通用编程的工具,它允许定义一种通用的类,可以用于不同的数据类型。
类模板使用关键字 template
开始,并在尖括号 < >
中包含一个或多个类型参数。类型参数可以在类定义的内部作为类型的占位符使用。例如:
template <typename T> class MyStack { private: T* elements; int top; int capacity; public: MyStack(int size) { elements = new T[size]; capacity = size; top = -1; } // 其他成员函数的实现省略... };
上述代码中的 MyStack
类模板定义了一个栈的数据结构,其中的元素类型 T
是一个占位符,在实际使用时会根据传入的类型进行实例化。
在实际使用类模板时,需要根据实际需求显式实例化特定类型的类。例如:
MyStack<int> intStack(10); // 实例化为 MyStack<int> MyStack<double> doubleStack(5); // 实例化为 MyStack<double>
在上述代码中,分别创建了一个 int
类型和一个 double
类型的 MyStack
对象。每个对象都是根据相应的类型实例化的类。
类模板还可以有多个类型参数,并且可以包含非类型参数,与函数模板类似。可以通过提供特定类型的实参来显式指定实例化的类型,也可以让编译器自动推导类型。
类模板还可以有成员函数、成员变量、构造函数和析构函数等,可以根据具体需求进行实现。在定义类模板的成员函数时,可以像普通类一样使用 T
来引用模板参数所表示的类型。
template <typename T> class MyStack { // ... void push(T element) { if (top + 1 >= capacity) { // 扩展容量代码... } elements[++top] = element; } T pop() { if (top < 0) { // 异常处理代码... } return elements[top--]; } // ... };
总结一下,类模板是C++中用于定义通用类的工具,允许创建可以适应不同数据类型的类。类模板使用关键字 template
和类型参数来定义,并通过显式实例化或类型推导来生成特定类型的类。类模板还可以包含成员函数、成员变量等,用于处理特定类型的数据。
用类模板定义对象
编译器由类模板生成类的过程叫类模板的实例化。由类模板实例化得到的类,叫模板类。
同一个类模板的两个模板类是不兼容的
同一个类模板的两个模板类是不兼容的
是的,对于同一个类模板,不同的模板实例(即不同的模板参数)会生成不同的类型,这些类型在编译时是完全独立和不兼容的。
例如,考虑下面的类模板 MyStack
:
template <typename T> class MyStack { // ... };
使用 MyStack<int>
实例化的对象和使用 MyStack<double>
实例化的对象是完全不同的类型,它们之间是不兼容的。
MyStack<int> intStack; MyStack<double> doubleStack; intStack.push(5); doubleStack.push(3.14); int x = intStack.pop(); // 类型为 int double y = doubleStack.pop(); // 类型为 double
在上述代码中,intStack
和 doubleStack
是两个完全不同的对象,它们的行为和类型都是根据实例化时的模板参数来确定的。
由于不同的模板实例生成的类型是不兼容的,因此不能将 MyStack<int>
的对象赋值给 MyStack<double>
的对象,也不能将它们混合使用。
MyStack<int> intStack; MyStack<double> doubleStack; // 以下代码是不允许的,会导致类型错误: doubleStack = intStack; // 错误:不兼容的类型 double x = intStack.pop(); // 错误:类型不匹配 intStack.push(3.14); // 错误:类型不匹配
因此,对于同一个类模板生成的不同模板实例,它们是不兼容的,并且在使用时需要注意保持类型一致。