I hope you have started to see the useful of templates, till now, I only showed templates with functions but, of course, that was just the start.
Now, I'll begin showing template classes. One of the most famous examples to show this are container classes, specially Stack. To change, I have done a Queue.
By the way, I have programmed using the exception library, if you don't get it, just ignored and think you could implement with other ways.
This way programming is so common in Java and if you come from it should be so easy to you.
First of all, what is the behave of a queue? just think with the queue of a cinema. First in, first out.
Example of the behave having this queue: [ a, b, c, d ]
With the back function we get d.
With a push(e) we'll have [ a, b, c, d, e ]
With pop(): [ b, c, d, e ]
empty(): false
size(): 4
front(): b
Well, here you have the code Note that I used a STL container internally.
#include <iostream>
#include <stdexcept>
#include <vector>
template <typename T>
class Queue{
private:
std::vector<T> _elements;
public:
Queue(){}//default constructor
T back(){ // return last element
if (_elements.empty()) {
throw std::out_of_range("Queue<>::back(): empty stack");
}
return _elements.back();
}
void push(const T& element){//add an element
_elements.push_back(element);
}
void pop(){//erase first element
if (_elements.empty()) {
throw std::out_of_range("Queue<>::pop(): empty stack");
}
_elements.erase( _elements.begin() );
}
bool empty(){return _elements.empty();}
int size(){return _elements.size();}
T front(){
if (_elements.empty()) {
throw std::out_of_range("Queue<>::front(): empty stack");
}
return _elements.front();}
};
template< class T >
//Note that we are passing by reference, so original queue will be erased
void iterate_Queue(Queue<T>& queue){
while(
not queue.empty()) {
std::cout << "(" << queue.front()<< ", " << queue.back() << ")";
std::cout << "with size:"<< queue.size() << std::endl;
queue.pop();
}
std::cout << std::endl;
}
typedef Queue<int> intQueue;// It's common to define typedef's in order to simply
//definicions
//typedef Queue< intQueue > intQueueQueue
;
typedef Queue< Queue<int> > intQueueQ
ueue;
// ^-- This space isn't opcional, copiler'd think that is a << operador
int main() {
try{
intQueue y;
y.push(1);
y.push(2);
y.push(3);
y.push(4);
iterate_Queue(y);
y.push(15);
iterate_Queue(y);
y.front();//exception, queue is empty
} catch (const std::exception& ex) {
std::cerr << "Exception: " << ex.what() << std::endl;
}
return 0;
}
output:
(1, 4)with size:4
(2, 4)with size:3
(3, 4)with size:2
(4, 4)with size:1
(15, 15)with size:1
Exception: Queue<>::front(): empty stack
RUN SUCCESSFUL (total time: 244ms)
Ok, I hope you understood the most code, let's take a look of hard parts.
First, I did a template function to empty and show the values of a Queue, printing the first and the last element.
Beside, we have some exceptions (throws) in our class, pop, back and front. Theses exceptions void the problem to access an empty vector, in that case, we make a throw:
throw std::out_of_range("Queue<>::pop(): empty stack");
Then, in the main, we bracketed our code with a try and catch. This means that C++ is going ahead taking care whether an error happends and if so then execute the catch. In fact, that error is called by the throw we made.
In the catch, ex.what() get the string we threw.
Another example of throw:
throw std::overflow_error("overflow");
even:
throw 20;//in that case you have to catch an int
On the other hand, I introduced typedef operator in which you can define any type as you like:
//typedef Queue< intQueue > intQueueQueue
;
typedef Queue< Queue<int> > intQueueQueue;
typedef Queue< std::vector<int
> > intVectorQ
ueue;I hope you enjoyed. For any doubt or comment, feel free to leave your message.
For an example without use of std::vector :
http://surprising-code.blogspot.com/p/example8.html
No comments:
Post a Comment