C++学习之模板 ----函数模板、类模板

本博文主要讨论函数模板与类模板以及其简单应用。

1)、作用:函数模板和类模板都可以看做是一种代码产生器,往里面放入具体的类型,得到具体化的函数或者class。

2)、编译(分为两步):

a):实例化之前,先检查模板本身语法是否正确;

b):根据 函数调用或者类模板调用 ,先去实例化模板代码,产生具体的函数/类。

也就是说, 没有函数调用或者类类型对象声明,就不会实例化模板代码,在目标文件obj中找不到模板的痕迹。

3):优缺点

模板的缺点是代码膨胀,编译速度慢,而优点是运行速度快。

一、函数模板:

1)、简单操作,代码实现如下:

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 using namespace std;
 5
 6 template <typename T>
 7 //修改办法2:
 8 //T max(const T &a, const T &b)
 9 const T &max(const T &a, const T &b)
10 {
11     return a > b ? a: b;
12 }
13
14 int main(int argc, const char *argv[])
15 {
16     cout << ::max(7, 42) << endl; //ok
17
18     cout << ::max(5.5, 7.8) << endl; //ok
19
20     string s1 = "hello";
21     string s2 = "world";
22     cout <<::max(s1, s2)<< endl; //ok
23
24     //cout << ::max("hello", "worldd");//error char[6],char[7]
25     //虽然都是字符数组,但是长度也是参数的一部分,故两者不是同一类型
26
27     //cout << ::max(3, 4.5) << endl;//error
28     //会发生编译错误,因为编译器推断第一个类型为int,第二个为double,没有一个模板符合这个要求,这样就会发生强制转换而产生一个局部的中间变量。这个变量的引用作为return的返回值;即此时我们引用了一个局部变量,这时会产生错误.
29        //修改办法1:
30     cout <<::max<int>(3, 6.5) << endl;
31     cout << ::max(3, static_cast<int>(6.5)) << endl;
32
33     return 0;
34 }

2)、一个非模板函数可以和一个同名的函数模板同时存在;两者可以因为参数不同而构成重载;

模板函数重载时,现则函数版本的一些特点:

a):条件相同时,选择非模板函数;例如 ::max(7,42);

b):在强制类型转化,与可行的实例化模板之间,优先选择实例化模板;例如::max(7.0,43.5),::max(‘a’,‘b’);

c):若实例化版本不可行,则尝试普通函数的转化,例如::max(‘a’。42.7)

d)参数是指针时,优先选择可实例化模板的引用版本,若不存在,则优先选择指针版本;

e):总之,尽可能采用最匹配、开销最小的版本。

示例代码及注释如下:

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 using namespace std;
 5
 6 //调用策略--->精准调用
 7 const int &max(const int &a, const int &b)
 8 {
 9     cout << "num.1"<< endl;
10     return a> b? a: b;
11 }
12
13 template <typename T>
14 const T *max(const T *a, const T *b)
15 {
16     cout <<"num.4" <<endl;
17     return *a > *b ? a:b ;
18 }
19
20 template <typename T>
21 const T &max(const T &a,const T &b)
22 {
23     cout << "num.2" << endl;
24     return a> b? a: b;
25 }
26
27 template <typename T>
28 const T &max(const T &a, const T &b, const T &c)
29 {
30     cout << "num.3"<< endl;
31     return ::max(::max(a, b), c);
32 }
33
34 int main(int argc, const char *argv[])
35 {
36     cout <<::max(7, 42, 68) << endl; //3 1 1
37     cout <<::max(7.0, 43.6) << endl; //2
38     cout <<::max(‘a‘,‘b‘) << endl;//2 char
39     cout <<::max(7, 42) << endl;//2
40     cout <<::max<>(7,42) << endl; //2 特定模板
41     cout << ::max<double>(7,42)<< endl;//2
42     cout <<::max(‘a‘, 43.7)<<endl; //1 强制转换
43
44     int a = 7;
45     int b = 89;
46     int *p1 = &a;
47     int *p2 = &b;
48     cout << ::max(p1, p2) << endl;//2 传引用,减少开销
49
50     return 0;
51 }

3)、值传递与引用传递的区别:

a):值传递与引用传递对于形参而言,本质区别在于是否产生了局部变量;

b):对于返回值而言,其区别在于, 返回时 是否产生了 临时变量。

c): 对于非引用类型 的参数, 在实参演绎的过程中,会出现 从数组衰退成指针(decay),从而丢失长度信息;而引用类型 则不会引发 衰退 decay。

在模板函数重载中, 不要混合使用值传递和引用传递,而且要尽可能使用引用。

示例代码及注释如下:

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 #include <string.h>
 5 using namespace std;
 6
 7 //传引用
 8 template <typename T>
 9 const T &max(const T &a, const T &b)
10 {
11     cout <<"num.1" << endl;
12     return a> b? a: b;
13 }
14
15 //传值
16 //修改1-->改为传引用
17 //const char *&max(const char *&a, const char *&b)
18 const char *max(const char *a, const char *b)
19 {
20     cout <<"num.2" << endl;
21     return ::strcmp(a, b)> 0? a: b;
22 }
23
24 //传引用
25 //修改2-->改为传值
26 //const T max(const T &a, const T &b, const T &c)
27 template <typename T>
28 const T &max(const T &a, const T &b, const T &c)
29 {
30     cout <<"num.3" << endl;
31     return ::max(::max(a, b), c);//return 临时变量
32     //这里将临时变量的引用返回出去,可能导致错误
33     //const char*tmp=::max(::max(s1,s2),s3)
34 }
35
36 int main(int argc, const char *argv[])
37 {
38     cout <<::max(7, 42, 68) << endl;
39
40     const char *s1 = "beij";
41     const char *s2 = "shangh";
42     const char *s3 = "shenzh";
43
44     cout <<::max(s1, s2, s3) << endl;
45
46     return 0;
47 }

二、类模板:

简单实现如下;

 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必须具备 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 #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 }

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 }
时间: 2024-10-29 19:11:17

C++学习之模板 ----函数模板、类模板的相关文章

泛函编程—模板函数_类模板

函数业务逻辑一样,只是函数参数类型不同函数模板的本质:类型参数化——泛型编程 语法: template <typename T> template <class T1,class T2>多个参数类型 类型 函数名(形式参数表) { 语句序列: } 函数模板基础: template是告诉C++编译器,开始泛型编程,看到T,不要随便报错 template <typename T>//一个模板 void myswap(T& a, T& b) { T c; c

C++ Primer 学习笔记_81_模板与泛型编程 --类模板成员[续1]

模板与泛型编程 --类模板成员[续1] 二.非类型形参的模板实参 template <int hi,int wid> class Screen { public: Screen():screen(hi * wid,'#'), cursor(hi * wid),height(hi),width(wid) {} //.. private: std::string screen; std::string::size_type cursor; std::string::size_type height

C++ Primer 学习笔记_82_模板与泛型编程 --类模板成员[续2]

模板与泛型编程 --类模板成员[续2] 六.完整的Queue类 Queue的完整定义: template <typename Type> class Queue; template <typename Type> ostream &operator<<(ostream &,const Queue<Type> &); template <typename Type> class QueueItem { friend clas

C++ Primer 学习笔记_80_模板与泛型编程 --类模板成员

模板与泛型编程 --类模板成员 引言: 这一节我们介绍怎样实现前面提到的Queue模板类. 标准库将queue实现为其他容器之上的适配器.为了强调在使用低级数据结构中设计的编程要点,我们将Queue实现为链表.实际上,在我们的实现中使用标准库可能是个更好的决定!!-_-. 1.Queue的实现策略 如图所示,我们实现两个类: 1)QueueItem类表示Queue的链表中的节点,该类有两个数据成员item和next: a. item保存Queue中元素的值,它的类型随Queue的每个实例而变化:

函数、类模板

泛型程序设计 算法实现时不指定具体要操作的数据的类型.适用于多种数据结构. 函数模板 Template <class 类型参数1,class类型参数2,…..> 返回值类型 模板名(形参表) { 函数体: } 函数模板可以重载,只要它们的形参表不同即可. C++编译器遵循以下优先顺序: 先找参数完全匹配的普通函数(非由模板实例化而得的函数) 再找参数完全匹配的模板函数 再找实参经过自动类型转换后能匹配的普通函数 上面的都不符合则报错. 可以在函数模板中使用多个类型参数,可以避免二义性. #in

C++ Primer 学习笔记_82_模板与泛型编程 -类模板成员[续二]

模板与泛型编程 --类模板成员[续2] 六.完整的Queue类 Queue的完整定义: template <typename Type> class Queue; template <typename Type> ostream &operator<<(ostream &,const Queue<Type> &); template <typename Type> class QueueItem { friend clas

C++ Primer 学习笔记_81_模板与泛型编程 -类模板成员[续一]

模板与泛型编程 --类模板成员[续1] 二.非类型形参的模板实参 template <int hi,int wid> class Screen { public: Screen():screen(hi * wid,'#'), cursor(hi * wid),height(hi),width(wid) {} //.. private: std::string screen; std::string::size_type cursor; std::string::size_type height

3.2 STL中的函数对象类模板

*: STL中有一些函数对象类模板,如下所示: 1)例如要求两个double类型的x 和y 的积,可以: multiplies<double>()(x,y); 该表达式的值就是x*y的值. 2)less是STL中最常用的函数对象类模板,其定义如下: template<class _Tp> struct less { bool oprator()(const _Tp&_x,const _Tp&_y)const { return _c<_y; } } 要判断两个i

类模板,多种类型的类模板,自定义类模板,类模板的默认类型,数组的模板实现,友元和类模板,友元函数,类模板与静态变量,类模板与普通类之间互相继承,类模板作为模板参数,类嵌套,类模板嵌套,类包装器

 1.第一个最简单的类模板案例 #include "mainwindow.h" #include <QApplication> #include <QPushButton> #include <QLabel> template<class T> class run { public: T w; void show() { w.show(); } void settext() { w.setText("A"); }

【C++】模板简述(三):类模板

上文简述了C++模板中的函数模板的格式.实例.形参.重载.特化及参数推演,本文主要介绍类模板. 一.类模板格式 类模板也是C++中模板的一种,其格式如下: template<class 形参名1, class 形参名2, ...class 形参名n> class 类名{ ... }; 例如:我个人模拟实现的Vector的声明 template<typename T> class Vector{ public: typedef T* Iterator; //迭代器 typedef c