1. 简介
我们对传递数值或变量给函数都很熟悉,除了传递变量,我们还能传递类型给模板。传递类型就是大家所熟知的泛型编程,因为 我们可以用泛型编写程序,而用特定的类型调用。
泛型编程的目的是为了编写的程序不依赖于数据类型。在 C 语言中,所有的代码都需要绑定到确定的数据类型,这样写的代码只能对特定的数据类型起作用。 而模板可以让我们实现泛型编程。你可以将类型作为参数来构建模板函数和类模板。当你的算法需要作用于多种数据类型的时候,模板就显得及其有用了。
C++的标准模板库(STL)提供了一些常用的容器类模板的实现,例如vector
,可以用来存放所有类型的元素。
2. 示例:STL 中的 vector 类模板
C/C++中的内置数组有一些缺点:
- 它的大小是固定的,需要在声明的时候确定大小,不支持动态声明。你不能在执行期给数组扩容;
- 数组不提供下标边界校验,你可以使用超出边界的下标
- 你需要自己实现数组比较,和赋值操作
C++提供了一个vector
类模板,作为标准模板库(STL)的一部分。vector
被定义在<vector>
头文件中,属于std
命名空间。vector 是最常用的 STL 类,它能够取代数组,并且支持动态分配空间和一些其它操作(例如比较和赋值)。
vector 是一个类模板,它可以被特定类型的实例化,形如:vector<int>
, vector<double>
, vector<string>
。同一个模板能够用于多种类型,而不必为每种类型都写一套实现。
1#include <iostream>
2#include <vector>
3#include <string>
4
5using namespace std;
6
7void print(const vector<int> &v);
8
9int main(int argc, char *argv[]) {
10 vector<int> v1(5); // Create a vector with 5 elements.
11
12 // Assign values into v1, using array-like index []
13 // You can retrieve the size of vector via size()
14 for (int i = 0; i < v1.size(); i++) {
15 v1[i] = (i + 1) * 2;
16 }
17
18 // Print vector content, using at()
19 for (int i = 0; i < v1.size(); i++) {
20 cout << v1.at(i) << " ";
21 }
22 cout << endl;
23
24 vector<int> v2;
25 // Assign v1 to v2 memberwise
26 v2 = v1;
27 for (int i = 0; i < v2.size(); i++) {
28 cout << v2[i] << " ";
29 }
30 cout << endl;
31
32 // Compare 2 vectors memberwise
33 cout << boolalpha << (v1 == v2) << endl;
34
35 // Append more elements - synamically allocate memory
36 v1.push_back(80);
37 v1.push_back(81);
38 for (int i = 0; i < v1.size(); i++) {
39 cout << v1[i] << " ";
40 }
41 cout << endl;
42
43 vector<string> v3;
44 v3.push_back("a for apple");
45 v3.push_back("b for boy");
46 for (int i = 0; i < v3.size(); i++) {
47 cout << v3[i] << " ";
48 }
49
50 cout << endl;
51
52 return 0;
53}
说明:
- 你可以通过声明
vector<int> v1(n)
来初始化一个int
类型的vector
,其中n
表示初始化的元素个数 - 可以使用
v1.size()
来获取元素个数 - 可以使用
v1[i]
或v1.at(i)
来访问元素,但是[]
操作符不会做边界检查,而at()
会 - 使用
push_back()
和pop_back()
添加和删除元素。vector
会自动调整内存分配。
3. 函数模板
把处理不同类型的公共逻辑抽象成函数,就得到了函数模板。
定义函数模板的定义语法如下:
1template <typename T> OR template <class T>
2return-type function-name(function-parameter-list) { ...... }
Example 1
1#include <iostream>
2
3using namespace std;
4
5template <typename T>
6void mySwap(T &a, T &b);
7
8int main(int argc, char *argv[]) {
9 int i1 = 1, i2 = 2;
10 mySwap(i1, i2);
11 cout << "i1 is " << i1 << ", i2 is " << i2 << endl;
12
13 char c1 = 'a', c2 = 'b';
14 mySwap(c1, c2);
15 cout << "c1 is " << c1 << ", c2 is " << c2 << endl;
16
17 double d1 = 1.1, d2 = 2.2;
18 mySwap(d1, d2);
19 cout << "d1 is " << d1 << ", d2 is " << d2 << endl;
20
21 return 0;
22}
23
24template <typename T>
25void mySwap(T &a, T &b) {
26 T temp;
27 temp = a;
28 a = b;
29 b = temp;
30}
C++编译器会为每种使用的类型都生成一个对应的函数,例如int
型:
这样并不能为代码的执行效率和内存使用率带来提升,但是能够大大提高开发效率。
Example 2
1#include <iostream>
2
3using namespace std;
4
5template<typename T>
6T abs(T value) {
7 T result;
8 result = (value >= 0) ? value : -value;
9 return result;
10}
11
12int main(int argc, char *argv[]) {
13 int i = -5;
14 cout << abs(i) << endl;
15
16 double d = - 55.5;
17 cout << abs(d) << endl;
18
19 float f = -555.5f;
20 cout << abs(f) << endl;
21
22 return 0;
23}
函数模板重载
1#include <iostream>
2
3using namespace std;
4
5template<typename T>
6void mySwap(T &a, T &b);
7
8template<typename T>
9void mySwap(T a[], T b[], int size);
10
11template<typename T>
12void print(const T *const array, int size);
13
14int main(int argc, char *argv[]) {
15 int i1 = 1, i2 = 2;
16 mySwap(i1, i2);
17 cout << "i1 is " << i1 << ", i2 is " << i2 << endl;
18
19 const int SIZE = 3;
20 int arr1[] = {1, 2, 3}, arr2[] = {4, 5, 6};
21 mySwap(arr1, arr2, SIZE);
22
23 print(arr1, SIZE);
24 print(arr2, SIZE);
25 return 0;
26}
27
28
29template<typename T>
30void mySwap(T &a, T &b) {
31 T temp;
32 temp = a;
33 a = b;
34 b = temp;
35}
36
37template<typename T>
38void mySwap(T a[], T b[], int size) {
39 T temp;
40 for (int i = 0; i < size; i++) {
41 temp = a[i];
42 a[i] = b[i];
43 b[i] = temp;
44 }
45}
46
47template<typename T>
48void print(const T *const array, int size) {
49 cout << "(";
50 for (int i = 0; i < size; i++) {
51 cout << array[i];
52 if (i < size - 1) cout << ",";
53 }
54 cout << ")" << endl;
55}
显式特化
1#include <iostream>
2
3using namespace std;
4
5template<typename T>
6void mySwap(T &a, T &b);
7
8template<>
9void mySwap<int>(int &a, int &b);
10
11int main(int argc, char *argv[]) {
12 double d1 = 1, d2 = 2;
13 mySwap(d1, d2);
14
15 int i1 = 1, i2 = 2;
16 mySwap(i1, i2);
17
18 return 0;
19}
20
21
22template<typename T>
23void mySwap(T &a, T &b) {
24 cout << "template" << endl;
25 T temp;
26 temp = a;
27 a = b;
28 b = temp;
29}
30
31template<>
32void mySwap<int>(int &a, int &b) {
33 cout << "specilization" << endl;
34 int temp;
35 temp = a;
36 a = b;
37 b = temp;
38}
4. 类模板
定义一个类模板的语法如下:
关键字’class’和’typename’都是用来定义模板的。使用定义好的模板的语法是:ClassName<actual-type>
例如:
1#include <iostream>
2using namespace std;
3
4template <typename T>
5class Number {
6private:
7 T value;
8public:
9 Number(T value) { this->value = value; }
10 T getValue() const { return this->value; }
11 void setValue(T value) { this->value = value; }
12};
13
14int main(int argc, char *argv[]) {
15 Number<int> i(55);
16 cout << i.getValue() << endl;
17
18 Number<double> d(55.66);
19 cout <<d.getValue() << endl;
20
21 Number<string> s("hello");
22 cout << s.getValue() << endl;
23 return 0;
24}
将模板声明和定义分开
如果将函数实现和声明分开,就需要在每个函数实现上都使用"template"关键字,例如:
将所有模板代码都放在头文件中
多参数类型
默认类型
特化
1// General Template
2template<typename T>
3class Complex { ...... }
4
5// Specialization for type double
6template<>
7class Complex<double> { ...... }
8
9// Specialization for type int
10template<>
11class Complex<int> { ...... }
5. 示例:MyComplex Template Class
MyComplex.h
1/*
2 * The MyComplex template class header (MyComplex.h)
3 * All template codes are kept in the header, to be included in program
4 * (Follow, modified and simplified from GNU GCC complex template class.)
5 */
6#ifndef MY_COMPLEX_H
7#define MY_COMPLEX_H
8
9#include <iostream>
10
11// Forward declaration
12template <typename T> class MyComplex;
13
14template <typename T>
15std::ostream & operator<< (std::ostream & out, const MyComplex<T> & c);
16template <typename T>
17std::istream & operator>> (std::istream & in, MyComplex<T> & c);
18
19// MyComplex template class declaration
20template <typename T>
21class MyComplex {
22private:
23 T real, imag;
24
25public:
26 // Constructor
27 explicit MyComplex<T> (T real = 0, T imag = 0)
28 : real(real), imag(imag) { }
29
30 // Overload += operator for c1 += c2
31 MyComplex<T> & operator+= (const MyComplex<T> & rhs) {
32 real += rhs.real;
33 imag += rhs.imag;
34 return *this;
35 }
36
37 // Overload += operator for c1 += value
38 MyComplex<T> & operator+= (T value) {
39 real += value;
40 return *this;
41 }
42
43 // Overload comparison == operator for c1 == c2
44 bool operator== (const MyComplex<T> & rhs) const {
45 return (real == rhs.real && imag == rhs.imag);
46 }
47
48 // Overload comparison != operator for c1 != c2
49 bool operator!= (const MyComplex<T> & rhs) const {
50 return !(*this == rhs);
51 }
52
53 // Overload prefix increment operator ++c
54 // (Separate implementation for illustration)
55 MyComplex<T> & operator++ ();
56
57 // Overload postfix increment operator c++
58 const MyComplex<T> operator++ (int dummy);
59
60 /* friends */
61
62 // (Separate implementation for illustration)
63 friend std::ostream & operator<< <>(std::ostream & out, const MyComplex<T> & c); // out << c
64 friend std::istream & operator>> <>(std::istream & in, MyComplex<T> & c); // in >> c
65
66 // Overloading + operator for c1 + c2
67 // (inline implementation for illustration)
68 friend const MyComplex<T> operator+ (const MyComplex<T> & lhs, const MyComplex<T> & rhs) {
69 MyComplex<T> result(lhs);
70 result += rhs; // uses overload +=
71 return result;
72 }
73
74 // Overloading + operator for c + double
75 friend const MyComplex<T> operator+ (const MyComplex<T> & lhs, T value) {
76 MyComplex<T> result(lhs);
77 result += value; // uses overload +=
78 return result;
79 }
80
81 // Overloading + operator for double + c
82 friend const MyComplex<T> operator+ (T value, const MyComplex<T> & rhs) {
83 return rhs + value; // swap and use above function
84 }
85};
86
87// Overload prefix increment operator ++c
88template <typename T>
89MyComplex<T> & MyComplex<T>::operator++ () {
90 ++real; // increment real part only
91 return *this;
92}
93
94// Overload postfix increment operator c++
95template <typename T>
96const MyComplex<T> MyComplex<T>::operator++ (int dummy) {
97 MyComplex<T> saved(*this);
98 ++real; // increment real part only
99 return saved;
100}
101
102/* Definition of friend functions */
103
104// Overload stream insertion operator out << c (friend)
105template <typename T>
106std::ostream & operator<< (std::ostream & out, const MyComplex<T> & c) {
107 out << '(' << c.real << ',' << c.imag << ')';
108 return out;
109}
110
111// Overload stream extraction operator in >> c (friend)
112template <typename T>
113std::istream & operator>> (std::istream & in, MyComplex<T> & c) {
114 T inReal, inImag;
115 char inChar;
116 bool validInput = false;
117 // Input shall be in the format "(real,imag)"
118 in >> inChar;
119 if (inChar == '(') {
120 in >> inReal >> inChar;
121 if (inChar == ',') {
122 in >> inImag >> inChar;
123 if (inChar == ')') {
124 c = MyComplex<T>(inReal, inImag);
125 validInput = true;
126 }
127 }
128 }
129 if (!validInput) in.setstate(std::ios_base::failbit);
130 return in;
131}
132
133#endif
TestMyComplex.cpp
1/* Test Driver for MyComplex template class (TestMyComplex.cpp) */
2#include <iostream>
3#include <iomanip>
4#include "MyComplex.h"
5
6int main() {
7 std::cout << std::fixed << std::setprecision(2);
8
9 MyComplex<double> c1(3.1, 4.2);
10 std::cout << c1 << std::endl; // (3.10,4.20)
11 MyComplex<double> c2(3.1);
12 std::cout << c2 << std::endl; // (3.10,0.00)
13
14 MyComplex<double> c3 = c1 + c2;
15 std::cout << c3 << std::endl; // (6.20,4.20)
16 c3 = c1 + 2.1;
17 std::cout << c3 << std::endl; // (5.20,4.20)
18 c3 = 2.2 + c1;
19 std::cout << c3 << std::endl; // (5.30,4.20)
20
21 c3 += c1;
22 std::cout << c3 << std::endl; // (8.40,8.40)
23 c3 += 2.3;
24 std::cout << c3 << std::endl; // (10.70,8.40)
25
26 std::cout << ++c3 << std::endl; // (11.70,8.40)
27 std::cout << c3++ << std::endl; // (11.70,8.40)
28 std::cout << c3 << std::endl; // (12.70,8.40)
29
30// c1+c2 = c3; // error: c1+c2 returns a const
31// c1++++; // error: c1++ returns a const
32
33// MyComplex<int> c4 = 5; // error: implicit conversion disabled
34 MyComplex<int> c4 = (MyComplex<int>)5; // explicit type casting allowed
35 std::cout << c4 << std::endl; // (5,0)
36
37 MyComplex<int> c5;
38 std::cout << "Enter a complex number in (real,imag): ";
39 std::cin >> c5;
40 if (std::cin.good()) {
41 std::cout << c5 << std::endl;
42 } else {
43 std::cerr << "Invalid input" << std::endl;
44 }
45 return 0;
46}
评论