我的C++实践(1):Stack的实现
2016-07-29 00:00
441 查看
(1)基本的Stack:以std::deque为内部容器。方法有入栈、出栈、返回栈顶元、判断栈空。
测试程序:
(2)改进1:指定栈的容量,要用到非类型模板参数。由于栈的容量固定,可以用数组来存放元素。
测试程序:
(3)改进2:可以指定栈内部使用的容器,还可以使 不同类型的栈之间能赋值。要用到模板模板参数。要定义一个赋值运算符的成员模板,它并不会覆盖缺省赋值运行符,对于相同类型栈之间的赋值,仍然会调用缺省的赋值运行符。由于容器有两个模板参数,一个是元素类型,一个是分配器类型,因此定义Stack的内部容器这个模板模板参数时,必须两个模板参数都要有。若只定义一个元素类型的参数,你传入容器(如vector,deque)时,并不能匹配。总之我们定义的模板模板参数必须与我们传入的模板精确匹配。
测试程序:
//stack1.h:栈的基本实现 #ifndef __STACK1_H__ #define __STACK1_H__ #include <deque> #include <stdexcept> template<typename T> class Stack{ private: std::deque<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<T>::pop(): empty stack"); } elems.pop_back(); //删除末端元素 } template<typename T> T Stack<T>::top() const{ if(elems.empty()){ throw std::out_of_range("Stack<T>::top(): empty stakc"); } return elems.back(); //返回末端元素的拷贝 } #endif
测试程序:
//stack1test.cpp:测试Stack #include <iostream> #include <string> #include <cstdlib> #include "stack1.h" int main(){ try{ Stack<int> intStack; //元素类型为int的栈 Stack<std::string> stringStack; //元素类型为string的栈 intStack.push(7); std::cout<<intStack.top()<<std::endl; stringStack.push("hello"); std::cout<<stringStack.top()<<std::endl; stringStack.pop(); stringStack.pop(); //会抛出std::out_of_range异常 }catch(std::exception const& ex){ //捕捉到这个异常并打印出来 std::cerr<<"Exception: "<<ex.what()<<std::endl; return EXIT_FAILURE; } }
(2)改进1:指定栈的容量,要用到非类型模板参数。由于栈的容量固定,可以用数组来存放元素。
//stack2.h:容量受限的栈 #ifndef __STACK2_H__ #define __STACK2_H__ #include <stdexcept> template<typename T,int size> class Stack{ T elems[size]; //由于容量受限,可用换成用数组来管理元素 int numElems; //元素的当前总个数 public: Stack(); //构造函数 void push(T const&); //压入元素 void pop(); //弹出元素 T top() const; //返回栈顶元素 bool empty() const{ //判断栈是否为空 return numElems==0; } bool full() const{ //判断栈是否已满 return numElems==size; } }; template<typename T,int size> Stack<T,size>::Stack():numElems(0){ //初始时不含元素 //不做任何事 } template<typename T,int size> void Stack<T,size>::push(T const& elem){ if(numElems==size) throw std::out_of_range("Stack<T,size>::push(): stack is full"); elems[numElems]=elem; //附加元素 ++numElems; //增加元素个数 } template<typename T,int size> void Stack<T,size>::pop(){ if(numElems<=0) throw std::out_of_range("Stack<T,size>::pop(): empty stack"); --numElems; //减少元素个数 } template<typename T,int size> T Stack<T,size>::top() const{ if(numElems<=0) throw std::out_of_range("Stack<>::top(): empty stack"); return elems[numElems-1]; //返回末端元素 } #endif
测试程序:
//stack2test.cpp:测试容量受限的Stack #include <iostream> #include <string> #include <cstdlib> #include "stack2.h" int main(){ try{ Stack<int,20> int20Stack; //可存储20个int元素的栈 Stack<int,40> int40Stack; Stack<std::string,40> stringStack; int20Stack.push(7); std::cout<<int20Stack.top()<<std::endl; int20Stack.pop(); stringStack.push("hello"); std::cout<<stringStack.top()<<std::endl; stringStack.pop(); stringStack.pop(); }catch(std::exception const& ex){ std::cerr<<"Exception: "<<ex.what()<<std::endl; return EXIT_FAILURE; } return 0; }
(3)改进2:可以指定栈内部使用的容器,还可以使 不同类型的栈之间能赋值。要用到模板模板参数。要定义一个赋值运算符的成员模板,它并不会覆盖缺省赋值运行符,对于相同类型栈之间的赋值,仍然会调用缺省的赋值运行符。由于容器有两个模板参数,一个是元素类型,一个是分配器类型,因此定义Stack的内部容器这个模板模板参数时,必须两个模板参数都要有。若只定义一个元素类型的参数,你传入容器(如vector,deque)时,并不能匹配。总之我们定义的模板模板参数必须与我们传入的模板精确匹配。
//stack3.hpp:可以指定内部容器,并且不同类型的栈之间可以赋值的栈 #ifndef STACK_HPP #define STACk_HPP #include <deque> #include <stdexcept> #include <memory> template<typename T,template<typename ELEM,typename ALLOC=std::allocator<ELEM> > class CONT=std::deque> class Stack{ CONT<T> elems; //存放元素的窗口 public: void push(T const&); //压入元素 void pop(); //弹出元素 T top() const; //返回栈顶元素 bool empty() const{ //判断栈是否为空 return elems.empty(); } /* 这个赋值运算符是一个独立的成员模板,它可以把Stack<T2,CONT2<ELEM2> >类型的栈赋值Stack<T,CONT<ELEM> >型的栈, 只要ELEM2型可以隐式转换成ELEM型。它并没有覆盖缺省赋值运行符,对于相同类型栈之间的赋值,仍然会调用缺省的赋值运行符 */ template<typename T2,template<typename ELEM2,typename ALLOC=std::allocator<ELEM2> > class CONT2> Stack<T,CONT>& operator=(Stack<T2,CONT2> const&); }; /* 由于并不需要用到参数ELEM和ALLOC,故可以省略 */ template<typename T,template<typename,typename> class CONT> void Stack<T,CONT>::push(T const& elem){ elems.push_back(elem); //把元素的拷贝添加到末端 } template<typename T,template<typename,typename> class CONT> void Stack<T,CONT>::pop(){ if(elems.empty()) throw std::out_of_range("Stack<>::pop(): empty stack"); elems.pop_back(); //删除末端元素 } template<typename T,template<typename,typename> class CONT> T Stack<T,CONT>::top() const{ if(elems.empty()) throw std::out_of_range("Stack<>::top(): empty stack"); return elems.back(); //返回末端元素的拷贝 } template<typename T,template<typename,typename> class CONT> template<typename T2,template<typename,typename> class CONT2> Stack<T,CONT>& Stack<T,CONT>::operator=(Stack<T2,CONT2> const& rhs){ if((void*)this==(void*)&rhs) //如果是自我赋值 return *this; Stack<T2,CONT2> tmp(rhs); elems.clear(); //删除所有现存的元素 while(!tmp.empty()){ //把rhs栈中元素拷贝到本栈中,并且元素的顺序不变 elems.push_front(tmp.top()); tmp.pop(); } return *this; } #endif
测试程序:
//stack3test.cpp:测试改进型栈的功能 #include <iostream> #include <string> #include <cstdlib> #include <vector> #include "stack3.hpp" int main(){ try{ Stack<int> intStack; //int栈 Stack<float> floatStack; //float栈 intStack.push(42); intStack.push(7); floatStack.push(7.7); floatStack=intStack; //不同类型的栈之间的赋值 std::cout<<floatStack.top()<<std::endl; floatStack.pop(); std::cout<<floatStack.top()<<std::endl; floatStack.pop(); std::cout<<floatStack.top()<<std::endl; floatStack.pop(); }catch(std::exception const& ex){ std::cerr<<"Exception: "<<ex.what()<<std::endl; } Stack<int,std::vector> vStack; //使用vector作为内部容器的int栈 vStack.push(42); vStack.push(7); std::cout<<vStack.top()<<std::endl; vStack.pop(); return 0; }
相关文章推荐
- C++ Primer学习系列(2):数组和指针/表达式/语句
- 我的C++实践(1):Stack的实现
- C++ Primer学习系列(2):数组和指针/表达式/语句
- C++ Primer学习心得和遇到的问题
- C++ Primer学习系列(5):复制控制/重载操作与转换/面向对象编程
- 我的C++实践(17):代理类技术
- C++ Primer学习系列(7):标准库名字和头文件/算法简介/再谈IO库
- C++ Primer学习心得和遇到的问题
- C++ Primer学习系列(5):复制控制/重载操作与转换/面向对象编程
- C++ Primer学习系列(6):模板与泛型编程/用于大型程序的工具/特殊工具与技术
- 我的C++实践(5):类型萃取技术
- 我的C++实践(17):代理类技术
- C++ Primer学习系列(7):标准库名字和头文件/算法简介/再谈IO库
- C++ Primer学习系列(6):模板与泛型编程/用于大型程序的工具/特殊工具与技术
- 我的C++实践(5):类型萃取技术
- C语言中将0到1000的浮点数用强制指针类型转换的方式生成一幅图像
- C++代码文件名标准化处理工具
- C++代码文件名标准化处理工具
- 解决ActiveMQ中,Java与C++交互中文乱码问题
- C++代码统计工具