由于将函数和类模板放在一块篇幅较大,我们今天将其拆分为两篇博文。
上篇博文我们讨论了函数模板的简单应用,本篇我们继续讨论模板的另一板块--类模板。
1)、作用:类模板类似于代码产生器,根据用户输入的类型不同,产生不同的class;
2)、编译:
a):检查模板class 的自身语法;
b):根据用户指定的类型 如 vector<string>,去实例化一个模板类。
注意: 不是实例化所以的代码,而仅仅实例化用户调用的部分;
类模板:简单实现如下;
1 #include <iostream>
2 #include <string>
3 #include <vector>
4 using namespace std;
5
6 template <typename T>
7 class Test
8 {
9 public:
10 Test(const T &s);
11 void print()const;
12
13 private:
14 T data_;
15 };
16
17 template <typename T>//若在类内部,则不必加Test<T>
18 Test<T>::Test(const T &s)
19 :data_(s)
20 { }
21
22 template <typename T>
23 void Test<T>::print()const
24 {
25 cout << data_ << endl;
26 }
27
28 int main(int argc, const char *argv[])
29 {
30 Test<int> t(12) ; // 比较vector<int> vec
31 t.print();
32
33 Test<string> t2("world");
34 t2.print();
35
36 return 0;
37 }
注意:STL库中,vector就是一个典型的类模板,vector<int>和vector<string>是两个完全不同的类,同样,vector 不是一个完整的类名,完整的类名如 vector<int> vec;vector必须具备 copy和assignment能力;
1 #include <iostream>
2 #include <string>
3 #include <vector>
4 using namespace std;
5
6 //class with no copy, no assignment
7 class Test
8 {
9 public:
10 Test() {};
11 ~Test() {};
12
13 private:
14 Test(const Test &);
15 void operator=(const Test &);
16 };
17
18 int main(int argc, const char *argv[])
19 {
20 vector<Test> vec; //运行此句时,没有产生错误
21 Test t;
22
23 vec.push_back(t);//此句产生错误。vector必须具备 copy和assignment 能力
24 return 0;
25 }
2):下面我们用stack栈的简单实现来说明类模板的一些问题;
1 //Stack.hpp
2 #ifndef STACK_H_
3 #define STACK_H_
4
5 #include <vector>
6 #include <stdexcept>
7
8 template <typename T>
9 class Stack
10 {
11 public:
12 void push(const T &t);
13 void pop();//出栈
14 T top()const; //查看栈顶
15 bool empty()const
16 { return elems_.empty(); }
17
18 private:
19 std::vector<T> elems_;
20 };
21
22 template <typename T>
23 void Stack<T>::push(const T &t)//将t放入vector中
24 {
25 elems_.push_back(t);
26 }
27
28 template <typename T>
29 void Stack<T>::pop()
30 {
31 if(! elems_.empty())
32 elems_.pop_back();
33 else
34 throw std::out_of_range("out of range");
35 }
36
37 template <typename T>
38 T Stack<T>::top()const
39 {
40 if(! elems_.empty())
41 return elems_.back();
42 else
43 throw std::out_of_range("out of range");
44 }
45 #endif
main.cpp
1 //main.cpp
2 #include "Stack.hpp"
3 #include <iostream>
4 #include <string>
5 #include <vector>
6 using namespace std;
7
8 int main(int argc, const char *argv[])
9 {
10 try
11 {
12 Stack<int> st;
13 st.push(7);
14 cout << st.top() << endl;
15
16 st.pop();
17 st.pop(); //throw
18 }
19 catch(exception &e)
20 {
21 cout << e.what()<< endl;
22 }
23 return 0;
24 }
注意以上我们将Stack 的声明与定义放在同一个文件中,原因是:将Stack 拆分成 h 和cpp 文件,构建时产生了链接错误;
a):模板的调用时机和代码的实例化必须放在同一时期;
b):编译stack.cpp时,编译器找不到任何用户调用的代码,所以得到的 stack.o 文件为空, 使用 nm -A stack.o | grep (函数)
c): 编译main.cpp时,编译器获取用户的调用,了解到应该去实例化 特定部分 代码 ,但是main.cpp中仅包含 .h 文件,编译器只能找到 模板的某些函数的声明,找不到其定义及实现。 所以推迟到 链接时期;
d);链接时期,由于 stack.o 文件为空,需要链接的代码没有产生。。
3);模板的特化:
1 //模板的特例化
2 #include "Stack.hpp"
3 #include <iostream>
4 #include <deque>
5 using namespace std;
6
7 template <> //模板特化
8 class Stack<string>
9 {
10 public:
11 void push(const string &s)
12 {
13 elems_.push_back(s);
14 }
15
16 void pop()
17 {
18 elems_.pop_back();
19 }
20 string top()const
21 {
22 return elems_.back();
23 }
24 bool empty()const
25 {
26 return elems_.empty();
27 }
28 private:
29 std::deque<string> elems_;
30 };
31
32 int main(int argc, const char *argv[])
33 {
34 try
35 {
36 Stack<string> st;
37 st.push("hello");
38 }
39 catch(exception &e)
40 {
41 cout << e.what() << endl;
42 }
43
44 return 0;
45 }
4):模板参数不仅可以为 类型, 而且可以为非类型(数值),需要注意的是,数值也是类名的一部分,例如 Stack<int, 5 > 和 Stack<int , 10> 不是同一类型。因此,二者的对象无法相互赋值。
缺省的模板参数:
Stack.hpp
1 //Stack.hpp 2 #ifndef STACK_H_ 3 #define STACK_H_ 4 5 #include <vector> 6 #include <stdexcept> 7 8 //缺省的模板参数 9 template <typename T, typename CONT = std::vector<T> > 10 class Stack 11 { 12 public: 13 void push(const T &t); 14 void pop();//出栈 15 T top()const; //查看栈顶 16 bool empty()const 17 { return elems_.empty(); } 18 19 private: 20 CONT elems_; 21 }; 22 23 template <typename T,typename CONT> 24 void Stack<T,CONT>::push(const T &t)//将t放入vector中 25 { 26 elems_.push_back(t); 27 } 28 29 template <typename T,typename CONT > 30 void Stack<T,CONT>::pop() 31 { 32 if(! elems_.empty()) 33 elems_.pop_back(); 34 else 35 throw std::out_of_range("out of range"); 36 } 37 template <typename T,typename CONT> 38 T Stack<T,CONT>::top()const 39 { 40 if(! elems_.empty()) 41 return elems_.back(); 42 else 43 throw std::out_of_range("out of range"); 44 } 45 #endif
main.cpp
1 #include "Stack.hpp"
2 #include <iostream>
3 #include <deque>
4 using namespace std;
5
6 int main(int argc, const char *argv[])
7 {
8 try
9 {
10 Stack<int> st;
11 st.push(7);
12 cout << st.top() << endl;
13 st.pop();
14
15 Stack<string, deque<string> > st2;
16 st2.push("hello");
17 st2.push("world");
18
19 while(!st2.empty())
20 {
21 cout << st2.top() << endl;
22 st2.pop();
23 }
24 }
25 catch(exception &e)
26 {
27 cout << e.what()<< endl;
28 }
29 return 0;
30 }
------------
非类型模板实现如下:
Stack.hpp
1 #ifndef STACK_H_
2 #define STACK_H_
3
4 #include <vector>
5 #include <stdexcept>
6
7 //非类型模板形参--->此处int
8 template <typename T, int MAXSIZE>
9 class Stack
10 {
11 public:
12 Stack();
13 void push(const T &t);
14 void pop();//出栈
15 T top()const; //查看栈顶
16
17 bool empty()const
18 { return numElems_ == 0; }
19 bool full()const
20 { return numElems_ ==MAXSIZE; }
21
22 private:
23 T elems_[MAXSIZE];
24 int numElems_;//当前元素数量
25 };
26
27 template <typename T, int MAXSIZE >
28 Stack<T,MAXSIZE>::Stack()
29 :numElems_(0)
30 {
31
32 }
33 template <typename T, int MAXSIZE >
34 void Stack<T,MAXSIZE>::push(const T &elem)//将t放入vector中
35 {
36 if(full())//成员函数的调用
37 throw std::runtime_error("full");
38 elems_[numElems_++] =elem ;
39 }
40
41 template <typename T, int MAXSIZE >
42 void Stack<T,MAXSIZE>::pop()
43 {
44 if(!empty())
45 --numElems_;//数据还存在
46 else
47 throw std::out_of_range("out of range");
48 }
49
50 template <typename T, int MAXSIZE >
51 T Stack<T,MAXSIZE>::top()const
52 {
53 return elems_[numElems_ -1];
54 }
55 #endif
main.cpp:
1 #include "Stack.hpp"
2 #include <iostream>
3 #include <deque>
4 using namespace std;
5
6 int main(int argc, const char *argv[])
7 {
8 try
9 {
10 Stack<int, 5> st;
11 st.push(14);
12 st.push(34);
13 st.push(45);
14 st.push(9);
15
16 cout << st.empty()<< endl;
17
18 Stack<int, 10> st2; // st 与 st2 类型不同
19 }
20 catch(exception &e)
21 {
22 cout << e.what()<< endl;
23 }
24 return 0;
25 }