继续昨天的学习,今天将要介绍的是,自己实现一个Stack的模板,然后在程序中实例化该模板,调用这个模板,以及使用模板函数优越性能的原因
1. 一个Stack 模板的例子代码
#include <vector>
#include <stdexcept>
//stack.hpp
template <typename T >
class Stack
{
private :
std::vector<T> elems ;
public :
void push ( T const & ) ;
void pop () ;
T top () const ;
bool empty() const
{
return elems.empty() ;
}
} ;
template <typename T >
void Stack<T>::push( T const & elem )
{
elems.push_back(elem) ;
}
template <typename T >
void Stack<T>::pop()
{
if ( elems.empty() )
throw std::out_of_range("Stack<>::poop : empty stack ") ;
elems.pop_back() ;
}
template <typename T>
T Stack<T>::top () const
{
if ( elems.empty() )
throw std::out_of_range("Stack<>::top : empty stack ") ;
return elems.back();
}
这段代码是这样的,首先创建一个模板类 Stack 其中这个栈是通过vector 来存放其中的元素的,里面定义了三个方法,分别是压栈、弹栈、和返回栈顶元素。已经得到证明的一点就是,在将元素从栈中删除但是同时又返回删除的元素这种做法会带来一定的安全隐患的,所以这里的pop操作仅仅是将栈中元素删除,而不提供将元素返回给调用者。
在压栈的时候,直接调用的是vector stl中的默认方法。而在弹栈和返回栈顶元素的方法实现体中首先要对栈是否为空进行检查,然后在调用vector中的方法来完成从栈中删除一个元素以及返回栈顶元素的功能。
2.调用Stack的代码例子
好了,下面介绍一下如何在自己编写的在运行期运行的程序,实例化模板程序以及调用其中的方法来完成特定的功能
//stl6_15.cpp
#include <iostream>
#include <string>
#include <cstdlib>
#include "stack1.hpp"
int main ()
{
try
{
Stack<int> intStack ;
Stack<std::string> stringStack ;
intStack.push(3) ;
std::cout<<intStack.top() <<std::endl ;
stringStack.push("hello") ;
std::cout<<stringStack.top()<<std::endl ;
stringStack.pop() ;
}
catch ( std::exception const & e )
{
std::cerr<<"Exception : "<<e.what()<<std::endl ;
return EXIT_FAILURE ;
}
system("pause") ;
return 0 ;
}
上面的程序是这样的, 首先分别创建两个stl 的Stack 一个是整数类型的另一个是字符串类型的,然后向这两个栈中分别压入元素,然后通过返回栈顶元素的方法来访问栈结构中的元素,在访问之后将其从栈中弹出。
在运行程序的时候,如果使用的是VS,有些不常用到的函数并没有在安装包中,这个时候需要通过
"调试->选项和设置->(左边)调试->常规->启动源服务器"
另一个就是“(左边)符号->(右边)勾选”微软符号服务器“ ”
通过这两项,就可以在程序运行的时候自动将程序中需要的缺省的程序库或是文件自动的下载到本地然后自动加载到程序中。这样程序就可以运行了。
3. 模板函数性能优越于一般函数的原因
通过声明类型Stack<int> , 在模板类内部就可以用int来实例化T。因此,intStack是一个创建自Stack<int> 的对象,它的元素存储于Stack中的vector中,这个vector 中支持的类型也是通过模板 T而设置为 int类型。对所有被调用的成员函数,都会实例化出基于int类型的函数代码。如果使用Stack<std::string>,将会创建一个 string 类型的 Stack, 其中的元素存储于vector中, 并且该vector 的类型是std::string 。 而对于所有被调用的成员函数,也会实例化出基于std::string的函数代码出来。
在这里需要注意的是, 只有那些被调用的成员函数,才会产生这样的函数的实例化代码, 对于类模板来说,成员函数只有在被使用的时候才会得到实例化。显然,这中实例化的方式可以节省很多的时间和空间; 同时另一个好处就是:对于那些“未提供所有成员函数中所有的操作的”类型, 你也可以使用该类型来实例化类模板,只要对那些“未能够提供某些操作的成员函数,进对其模板头进行相关符合规定的声明即可,而模板内部不对其提供实现实体即可。 例如, 某些类模板中的成员函数会使用operator<来对元素进行排序:如果不调用这些”使用operator<“ 的成员函数,那么对于没有定义operator< 的类型,也可以被用来实例化该类的模板。
在上面的例子中,缺少的是构造函数,push() top() 都被实例化了一个 int 和string的版本, 而对于 pop() 仅仅被实例化了一个string的版本,为什么呢? 因为在程序中没有对所创建的 int 类型的实例 intStack 调用pop方法,所以在程序编译执行的时候是不会生成与int类型相关的pop 的操作的。 但是,另一方面,如果在定义模板类中,定义了静态的成员的话,那么无论在实际使用中是否会用到这些静态成员(方法, 变量),用来实例化的每一种类型都是会实例化这些静态成员的。
先到这,有时间再写,然后我突然觉得,在早上的时候,可以做点数学题,这样相当于给大脑做一个晨练,一天的脑力可以得到很好的发挥,从明天开始实行(笑) ~