C++中template的简单用法

  

  模板(Template)指C++程序设计设计语言中采用类型作为参数的程序设计,支持通用程序设计。C++ 的标准库提供许多有用的函数大多结合了模板的观念,如STL以及IO Stream。使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。

一、函数模板

  在c++入门中,很多人会接触swap(int&, int&)这样的函数类似代码如下:

1 void swap(int&a , int& b) {
2     int temp = a;
3     a =  b;
4     b = temp;
5 }

  但是如果是要支持long,string,自定义class的swap函数,代码和上述代码差不多,只是类型不同,这个时候就是我们定义swap的函数模板,就可以复用不同类型的swap函数代码,函数模板的声明形式如下:

template<class 形参名, class 形参名,......> 返回类型 函数名(参数列表)
{

}

template<typename 形参名, typename 形参名,......> 返回类型 函数名(参数列表)
{

}

  swap函数模板的声明和定义代码如下: 

1 //method.h
2 #pragma once
3 template <class T> void swap(T& t1, T& t2)
4 {
5     T tmp;
6     tmp = t1;
7     t1 = t2;
8     t2 = tmp;
9 }

  上述是模板的声明和定义了,那模板如何实例化呢,模板的实例化是编译器做的事情,与程序员无关,那么上述模板如何使用呢,代码如下:

 1 //main.cpp
 2 #include <stdio.h>
 3 #include "method.h"
 4 int main() {
 5     //模板方法
 6     int num1 = 1, num2 = 2;
 7     swap<int>(num1, num2);
 8     printf("num1:%d, num2:%d\n", num1, num2);
 9     return 0;
10 }

  可以注意到,我将函数模板的声明和定义都放在了头文件中,一般来说,函数的声明放在头文件,其定义通常是放在源文件中的,这样在包含头文件时,可以有效避免出现重定义error。但模板的使用,需要实例化,而当实例化一个模板时,编译器必须看到模板确切的定义,而不仅仅是它的声明。(这一部分更多的解释可参考http://blog.csdn.net/lichengyu/article/details/6792135

二、类模板

  考虑我们写一个简单的栈的类,这个栈可以支持int类型,long类型,string类型等等,不利用类模板,我们就要写三个以上的stack类,其中代码基本一样,通过类模板,我们可以定义一个简单的栈模板,再根据需要实例化为int栈,long栈,string栈。

 1 //stack.h
 2 #pragma once
 3 template<class T> class Stack {
 4 public:
 5     Stack();
 6     ~Stack();
 7     void push(T t);
 8     T pop();
 9     bool isEmpty();
10 private:
11     T* m_pT;
12     int m_maxSize;
13     int m_size;
14 };
15
16 //同样,也把类模板中函数的定义写在.h文件中
17
18 //Define of Construct
19 template<class T> Stack<T>::Stack()
20 {
21     m_maxSize = 100;
22     m_size = 0;
23     m_pT = new T[m_maxSize];
24 }
25
26 //Define of Destructor
27 template<class T> Stack<T>::~Stack()
28 {
29     delete[] m_pT;
30 }
31
32 //Define of push()
33 template<class T> void Stack<T>::push(T t)
34 {
35     m_size++;
36     m_pT[m_size - 1] = t;//数组下标从0开始
37 }
38
39 //Define of pop()
40 template<class T> T Stack<T>::pop()
41 {
42     T t = m_pT[m_size - 1];
43     m_size--;
44     return t;
45 }
46
47 //Define of isEmpty();
48 template<class T> bool Stack<T>::isEmpty()
49 {
50     return m_size == 0;
51 }

  上述定义了一个类模板--栈,这个栈很简单,只是为了说明类模板如何使用而已,最多只支持100个元素入栈,使用示例如下:

 1 #include <stdio.h>
 2 #include "stack.h"
 3
 4 int main()
 5 {
 6     Stack<int> intStack;
 7     intStack.push(1);
 8     intStack.push(2);
 9     intStack.push(3);
10
11     while (!intStack.isEmpty())
12     {
13         printf("num:%d\n", intStack.pop());
14     }
15     return 0;
16
17 }
18 //num:3
19 //num:2
20 //num:1

三、模板参数

  模板可以有类型参数,也可以有非类型参数(即内置类型参数),也可以有模板参数。

  上述类模板的栈有一个限制,就是最多只能支持100个元素,我们可以使用模板参数配置这个栈的最大元素数,如果不配置,就设置默认最大值为100,代码如下:

 1 //stack.h
 2 #pragma once
 3 //模板参数中使用内置的类型参数int,增加栈的最大容纳元素数
 4 template<class T,int maxsize=100> class Stack {
 5 public:
 6     Stack();
 7     ~Stack();
 8     void push(T t);
 9     T pop();
10     bool isEmpty();
11 private:
12     T* m_pT;
13     int m_maxSize;
14     int m_size;
15 };
16
17 //同样,也把类模板中函数的定义写在.h文件中
18
19 //Define of Construct
20 template<class T,int maxsize> Stack<T,maxsize>::Stack()    //注意Stack<T,maxsize>中maxsize不要漏了
21 {
22     m_maxSize =maxsize;
23     m_size = 0;
24     m_pT = new T[m_maxSize];
25 }
26
27 //Define of Destructor
28 template<class T,int maxsize> Stack<T,maxsize>::~Stack()
29 {
30     delete[] m_pT;
31 }
32
33 //Define of push()
34 template<class T,int maxsize> void Stack<T,maxsize>::push(T t)
35 {
36     m_size++;//增加元素的个数
37     m_pT[m_size - 1] = t;//入栈,因为数组下标从0开始,所以-1
38 }
39
40 //Define of pop()
41 template<class T,int maxsize> T Stack<T,maxsize>::pop()
42 {
43     T t = m_pT[m_size - 1];
44     m_size--;
45     return t;
46 }
47
48 //Define of isEmpty();
49 template<class T,int maxsize> bool Stack<T,maxsize>::isEmpty()
50 {
51     return m_size == 0;
52 }

使用示例如下:

 1 #include <stdio.h>
 2 #include "stack.h"
 3
 4 int main()
 5 {
 6     const int maxsize = 1024;
 7     Stack<int,1024> intStack;
 8     for (int i = 0; i < maxsize; i++)
 9         intStack.push(i);
10
11     while (!intStack.isEmpty())
12     {
13         printf("num:%d\n", intStack.pop());
14     }
15     return 0;
16
17 }
18 //num:1023
19 //num:1022
20 //num:1021
21 //...
22 //...
23 //num:0

:上述代码中template<class T,int maxsize=100> class Stack{};中int maxsize就是非类型的模板参数。有以下几点需要注意的:

  1、非类型参数在模板定义的内部是常量值,也就是说非类型参数在模板的内部是常量;

  2、非类型模板的参数只能是整型指针引用,像double,String,String **这样的类型是不允许的。但是double &,double *,对象的引用或指针是正确的;

  3、调用非类型模板形参的实参必须是一个常量表达式,即它必须能在编译时计算出结果;

  4、任何局部对象,局部变量,局部对象的地址,局部变量的地址都不是一个常量表达式,都不能用作非类型模板形参的实参。全局指针类型,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参。

  5、全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参;

  6、sizeof表达式的结果是一个常量表达死,也能用作非类型模板形参的实参。

时间: 2024-08-26 13:13:06

C++中template的简单用法的相关文章

java中Object.equals()简单用法

/* equals()方法默认的比较两个对象的引用! */ class Child { int num; public Child(int x){ num = x; } //人文的抛出运行时异常的好处是:可以自定义错误信息! /*public boolean equals(Object o) throws ClassCastException{ if(!(o instanceof Child)) throw new ClassCastException("中文提示:类型错误"); Ch

Android中AsyncTask的简单用法【转】

在开发Android移动客户端的时候往往要使用多线程来进行操作,我们通常会将耗时的操作放在单独的线程执行,避免其占用主线程而给用户带来不好的用户体验.但是在子线程中无法去操作主线程(UI 线程),在子线程中操作UI线程会出现错误.因此android提供了一个类Handler来在子线程中来更新UI线程,用发消息的机制更新UI界面,呈现给用户.这样就解决了子线程更新UI的问题.但是费时的任务操作总会启动一些匿名的子线程,太多的子线程给系统带来巨大的负担,随之带来一些性能问题.因此android提供了

C# Winfom 中ListBox的简单用法

Winform控件ListBox的用法 1.如何添加listBox的值 this.listBox1.Items.Add("张晓东"); 2.如何判断listBox集合是否添加过 //检查添加值是否添加过 if(this.listBox1.items.Contains("张晓东")){ MessageBox.show("集合成员已添加过!"); } else{ //执行添加集合成员 } 3.如何获取listBox选中的值 //判断所有选中项集合大于

(数据科学学习手札54)Python中retry的简单用法

一.简介 retry是一个用于错误处理的模块,功能类似try-except,但更加快捷方便,本文就将简单地介绍一下retry的基本用法. 二.基本用法 retry: 作为装饰器进行使用,不传入参数时功能如下例所示: from retry import retry @retry() def demo(): print('错误') raise demo() 我们编写了每次运行都会通过raise报错的自定义函数demo(),利用默认参数的retry()进行装饰,运行结果如下: 可以看到,retry()

swift中collectionView的简单用法

之前写过OC中collectionView的用法,现在再看看swift中collectionView的用法,有兴趣的朋友,可以两者前后比较下区别,swift现在没有稳定下来,语法更新的比较快,但是它核心的一些东西,已经定型了.这些还是靠读者们自己去挖掘吧. //这里签署数据源和代理,此时不需要引入layout的代理,也可以.class AmonViewController: UIViewController ,UICollectionViewDataSource,UICollectionView

easyui中parser的简单用法

在easyUI中,parser是在页面加载完成之后自动加载,将代码根据class渲染为不同的插件.除了自动加载之后,编程人员还可以使用手动调用的方式,比如$.parser.parse("#id")的方式进行调用,这样就可以允许编程人员根据不同需求动态加入不同的插件的代码,再使用该段代码的唯一id,利用parser进行渲染,比如插入插件代码至页面,代码如下: <div class="easyui-accordion" id="tt">

C#Winform中ToolTip的简单用法,

ToolTip它能够为我们的软件提供非常漂亮的提示信息,提高软件的可用性,给用户比较好的体验. 使用,在窗体加载时加载以下代码: var toolTip1 = new ToolTip(); toolTip1.AutoPopDelay = 10000; toolTip1.InitialDelay = 500; toolTip1.ReshowDelay = 500; toolTip1.ShowAlways = true; toolTip1.SetToolTip(this.label26, @"系统所

java中swing的简单用法,做一个小界面

package zzn; import javax.swing.*; public class demoui extends JFrame { public static void main(String[] args) { demoui ui=new demoui(); } public demoui() { this.setVisible(true); this.setSize(500,500); } }

Visual C++ 6.0中if的简单用法

1 # include<stdio.h> 2 int main (void) 3 { 4 if (3 > 2) 5 printf("AAAA"); 6 printf("BBBB"); 7 return 0; 8 } 9 //此时结果是,输出AAAA 和 BBBB,因为if只能控制一个语句 上面的第4行和第五行是一个语句,if只能控制一个语句,所以后面的BBBB不归if管,所以BBBB要输出来 1 # include<stdio.h> 2