C++模板介绍
C++ 模板
C++ 模板是一种强大的泛型编程工具,它允许我们编写通用的代码,可以用于处理多种不同的数据类型。模板允许我们在编写代码时将类型作为参数进行参数化,从而实现代码的重用性和灵活性。
在 C++ 中,模板由关键字 template
开始,并且后面跟着模板参数列表。模板参数可以是类型参数或非类型参数。
1、模板的基本语法
1.1 类型模板参数(函数模板)
类型模板参数允许我们在定义模板时指定一个或多个类型参数,这些类型参数可以在模板的定义中使用。例如,下面是一个简单的模板函数示例:
template <typename T> T add(T a, T b) { return a + b; } int main() { std::cout << add<int>(1,1); // 输出:2 显示指定模板参数类型 std::cout << add(1.1, 2.2); // 输出:3.3 自动推导模板参数类型为浮点型 return 0; }
在这个例子中,T
是类型模板参数,它代表一个占位符类型。我们可以在模板函数 add
中使用 T
来进行参数和返回类型的声明。当我们调用 add
函数时,编译器会根据传入的实际类型来推断 T
的值。
1.2 非类型模板参数(类模板)
非类型模板参数允许我们在定义模板时指定一个或多个非类型参数,这些参数可以是整数、枚举、指针或引用。非类型参数的值在编译时确定,且在模板的每个实例化中都是常量。例如,下面是一个使用非类型参数的模板类示例:
template <int Size> class Array { private: int data[Size]; public: // 构造函数 Array() { for (int i = 0; i < Size; ++i) { data[i] = i; } } void print() const { for (int i = 0; i < Size; i++) { std::cout << data[i] << " "; } std::cout << std::endl; } }; int main() { Array<5> arr5; Array<10> arr10; arr5.print();// 输出:0 1 2 3 4 arr10.print();// 输出:0 1 2 3 4 5 6 7 8 9 return 0; }
在这个例子中,Size
是非类型模板参数,它代表数组的大小。我们可以在模板类 Array
的定义中使用 Size
来声明数组的大小,并在构造函数中初始化数组。
2、模板作用及优势
模板在 C++ 中具有重要的作用和优势,可以提高代码的重用性和灵活性。
2.1.代码重用:
模板允许我们编写通用的代码,可以处理多种不同的数据类型,而无需为每种类型编写重复的代码。下面是一个模板函数示例,用于计算数组的总和:
#include <iostream> template <typename T, size_t Size> T sumArray(T (&arr)[Size]) { T sum = T(); for (size_t i = 0; i < Size; ++i) { sum += arr[i]; } return sum; } int main() { int intArray[] = {1, 2, 3, 4, 5}; double doubleArray[] = {1.1, 2.2, 3.3, 4.4, 5.5}; int intSum = sumArray(intArray); double doubleSum = sumArray(doubleArray); std::cout << "Sum of intArray: " << intSum << std::endl; std::cout << "Sum of doubleArray: " << doubleSum << std::endl; return 0; }
在这个示例中,我们定义了一个模板函数 sumArray
,它可以接受任意类型的数组作为参数,并计算数组的总和。通过使用模板,我们可以在不修改代码的情况下重复使用这个函数,适用于不同类型的数组。
2.2.类型安全:
模板在编译时进行类型检查,可以提供更好的类型安全性。下面是一个模板类示例,用于实现一个简单的栈数据结构:
#include <iostream> #include <vector> template <typename T> class Stack { private: std::vector<T> stack; public: void push(const T& item) { stack.push_back(item); } T pop() { if (stack.empty()) { throw std::runtime_error("Stack is empty"); } T item = stack.back(); stack.pop_back(); return item; } bool isEmpty() const { return stack.empty(); } }; int main() { Stack<int> intStack; intStack.push(10); intStack.push(20); intStack.push(30); while (!intStack.isEmpty()) { std::cout << intStack.pop() << " "; } // 输出: 30 20 10 std::cout << std::endl; Stack<std::string> stringStack; stringStack.push("Hello"); stringStack.push("World"); while (!stringStack.isEmpty()) { std::cout << stringStack.pop() << " "; } // 输出: World Hello return 0; }
在这个示例中,我们定义了一个模板类 Stack
,它可以存储任意类型的元素。通过使用模板,我们可以在编译时检查类型的一致性,并避免将错误类型的元素推入栈中。
3、C++中的模板与 Java中的泛型的异同
C++中的模板和 Java中的泛型都是泛型编程的概念,它们都可以用于编写通用的代码,以便在多个类型上重复使用。
3.1.相同点
-
都可以使用泛型来编写通用的代码,以便在多个类型上重复使用。
-
都允许在编译时进行类型检查,以避免在运行时出现类型错误。
-
都可以使用类型参数来表示通用的类型。
3.2.主要不同点
-
语法不同:C++中的模板使用template关键字来声明模板参数,而 Java中的泛型使用<>符号来声明类型参数。
-
支持的类型不同:在C++中,模板可以使用任何可用的类型,包括内置类型和自定义类型,而在 Java中,泛型不能接受基本类型作为类型参数――它只能接受引用类型。这意味着可以定义
List<Integer>
,但是不可以定义List<int>
。 -
C++中,参数类型不同,实例类型也不同。而在 Java中,不管类型参数是什么,所有实例都是同一类型,并且类型参数会在运行时被抹去。即,尽管在编译时
ArrayList<String>
和ArrayList<Integer>
是两种类型,但是在运行时只有ArrayList被加载到 JVM中。 -
Java中,在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但是在静态方法中不能使用类的泛型(因为类的泛型在创建对象时,即实例化时才指定,而静态方法要早于对象的创建,此时类的泛型还没指定而静态结构已经需要使用了)。C++中,类型参数可以用于静态方法和静态变量。
.....
综上所述,虽然C++中数模板和 Java中的泛型都是泛型编程的概念,但它们在实现上有很大差异。
参考资料:
【C++基础语法】
https://blog.csdn.net/hxhxhxhxx/article/details/119334165
【C++模板和泛型详解】
https://blog.csdn.net/jj6666djdbbd/article/details/127155728
【C++泛型和 Java泛型的异同】
https://blog.csdn.net/cnds123/article/details/130778765
https://blog.csdn.net/coding_is_fun/article/details/81564512