C++学习之模板 (二) -----类模板

由于将函数和类模板放在一块篇幅较大,我们今天将其拆分为两篇博文。

上篇博文我们讨论了函数模板的简单应用,本篇我们继续讨论模板的另一板块--类模板。

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 }
时间: 2024-08-02 07:03:29

C++学习之模板 (二) -----类模板的相关文章

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

本博文主要讨论函数模板与类模板以及其简单应用. 1).作用:函数模板和类模板都可以看做是一种代码产生器,往里面放入具体的类型,得到具体化的函数或者class. 2).编译(分为两步): a):实例化之前,先检查模板本身语法是否正确: b):根据 函数调用或者类模板调用 ,先去实例化模板代码,产生具体的函数/类. 也就是说, 没有函数调用或者类类型对象声明,就不会实例化模板代码,在目标文件obj中找不到模板的痕迹. 3):优缺点 模板的缺点是代码膨胀,编译速度慢,而优点是运行速度快. 一.函数模板

Django学习笔记(二)—— 模板

疯狂的暑假学习之 Django学习笔记(二)-- 模板 参考: <The Django Book> 第四章 一.模板基础知识 1.模板是如何工作的 用 python manage.py shell 启动交互界面(因为manage.py 保存了Django的配置,如果直接python启动交互界面运行下面代码会出错) 输入下面代码 >>> from django import template >>> t = template.Template('My name

函数模板与类模板

函数模板,顾名思义,是在生成函数时依照的模板. 有时,我们需要对不同的数据类型做同样的函数操作. 比如:分别对一个int类型数 和 一个double类型数求平方. 这时,虽然都是同样的求平方操作(函数体内代码一样),但是我们必须要编写两个不同的函数,因为处理int类型的函数的参数和返回值类型都应该是int,而处理double类型的函数的参数和返回值都应该是double. 如下:函数体内操作代码一样,设计为重载函数,用相同的函数名,但是参数类型和返回值类型却都不一样,函数需要定义两次. int S

C++笔记(7):泛型编程和模板(函数模板和类模板)

泛型编程和模板 0.泛型编程 1.函数模板 2.类模板 ----------------------------------------------------------------------------------------------------------- 0.泛型编程 所谓泛型就是以独立于任何特定类型的方式编写代码.前面介绍的标准库的容器.迭代器和算法都是泛型编程的具体应用. 模板是泛型编程的基础.使用模板的时候不需要知道模板是如何定义的,但今天我们来介绍如何定义自己的模板类和模

【C/C++学院】0825-类模板/final_override/类模板与普通类的派生类模板虚函数抽象模板类/类模板友元/位运算算法以及类声明/Rtti 实时类型检测/高级new创建/类以及函数包装器

类模板 类模板多个类型默认类型简单数组模板 #pragma once template <class T=int>//类模板可以有一个默认的值 class myArray { public: myArray(); ~myArray(); }; #include "myArray.h" template <class T=int>//每一个函数都需要加上一个默认的值 myArray<T>::myArray() //类模板成员函数在外部,需要加载类型初始

C++模板(类模板、函数模板)

1.在c++Template中很多地方都用到了typename与class这两个关键字,而且好像可以替换,是不是这两个关键字完全一样呢? 答:class用于定义类,在模板引入c++后,最初定义模板的方法为:template<class T>,这里class关键字表明T是一个类型,后来为了避免class在这两个地方的使用可能给人带来混淆,所以引入了typename这个关键字,它的作用同class一样表明后面的符号为一个类型,这样在定义模板的时候就可以使用下面的方式了:      template

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

 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++解析(26):函数模板与类模板

0.目录 1.函数模板 1.1 函数模板与泛型编程 1.2 多参数函数模板 1.3 函数重载遇上函数模板 2.类模板 2.1 类模板 2.2 多参数类模板与特化 2.3 特化的深度分析 3.小结 1.函数模板 1.1 函数模板与泛型编程 C++中有几种交换变量的方法? 交换变量的方法--定义宏代码块 vs 定义函数: 定义宏代码块 优点:代码复用,适合所有的类型 缺点:编译器不知道宏的存在,缺少类型检查 定义函数 优点:真正的函数调用,编译器对类型进行检查 缺点:根据类型重复定义函数,无法代码复

C++ template学习二 类模板定义及实例化

一个类模板(也称为类属类或类生成类)允许用户为类定义一种模式,使得类中的某些数据成员.默写成员函数的参数.某些成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的). 如果一个类中数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就必须将此类声明为模板,它的存在不是代表一个具体的.实际的类,而是代表着一类类. //templatedemo.h#ifndef TEMPLATE_DEMO_HXX#define TEMPLATE_DEMO_HXX template

C++模板学习:函数模板、结构体模板、类模板

C++模板:函数.结构体.类 模板实现 1.前言:(知道有模板这回事的童鞋请忽视) 普通函数.函数重载.模板函数 认识. //学过c的童鞋们一定都写过函数sum吧,当时是这样写的: int sum(int a,int b) { return a+b; } //实现了整数的相加 //如果再想同时实现小数的相加,就再多写个小数的相加.普通实现我就不写了,知道函数重载的童鞋们会这样写: int sum(int a,int b) {//第一个function return a+b;} double su