Understanding Class Templates in C++
Code duplication is bad for your code. Think of it as a malignant tumor waiting to grow into a cancer. C++ offers Class Templates to avoid code duplication.
Like function templates, class templates provide class behavior for different types. For example, you can create a container class which can manage elements of a certain type.
First, let me show you how declare a class template and its implementation. Then I will show you how to create a class template.
Declaring a Class Template
Before the class declaration, a statement declares an identifier as a type parameter. For example:
template <typename T>
class MyClass{
//...
};
Inside the class template, you can use the parameter T as any type to declare members and methods. For example:
template <typename T>
class MyClass{
private:
std::vector<T> myElements;
public:
MyClass(); //constructor
void push(T const& element);
T getElement();
};
If you have to declare your own copy constructor and copy assignment, it would look like this:
template <typename T>
class MyClass{
//...
MyClass (MyClass<T> const& u); //copy constructor
MyClass<T>& operator=(MyClass<T> const& u); //copy assignment
};
Implementation of Member functions
Member functions are specified as function templates. And you have to specify the full type qualification of the class template. For example:
template <typename T>
void MyClass::push(T const& element){
//...
}
Implementing a Class Template Stack
Let's create a class template that will mimic the functionality of a stack algorithm. This class will contain a vector container whose type depends on the template parameter.
template <typename T>
class Stack{
private:
std::vector<T> elements; //elements
public:
Stack(){};
~Stack(){};
void push(T const& uElement); //push element
void pop(); //remove last element
T top() const; //return last element
};
The class provides a vector container named elements along with three member functions.
The listing below shows the implementation of the function members.
template <typename T>
void Stack<T>::push(T const& uElement){
elements.push_back(uElement); //append the element
}
template <typename T>
void Stack<T>::pop(){
//Normally you would do a check if the vector is not empty. Avoiding for simplicity
elements.pop_back(); //remove last element
}
template <typename T>
T Stack<T>::top() const{
//Normally you would do a check if the vector is not empty. Avoiding for simplicity
return elements.back(); //return copy of last element
}
Using the Class Template Stack
To use a class template object, specify the template arguments explicitly. For example:
int main(int argc, const char * argv[]) {
//create a stack of int type
Stack<int> myIntStack;
//append an integer to the container
myIntStack.push(2);
//create a stack of float type
Stack<float> myFloatStack;
//append a float to the container
myFloatStack.push(4.2);
return 0;
}
A class template allows you to create several versions of a class which can deal with different types. The Stack class above not only can deal with ints and floats but also with strings.
As you can see, Class Templates are simple to set up. Use them to avoid code duplication.
The Inclusion Model
If you are like any other developer, you will want to separate your template class into a declaration file (.h) and an implementation file (.cpp). However, doing so will normally result in a compiler error.
To fix this error you need to #include your implementation file at the end of your header file as shown below:
#ifndef Stack_hpp
#define Stack_hpp
#include <stdio.h>
#include <vector>
#include <stdexcept>
#include <iostream>
template<typename T>
class Stack{
private:
std::vector<T> elements; //elements of container
public:
Stack(); //constructor
void push(T const& uElement); //push element
T top()const; //return top element
};
#include "Stack.cpp" //#Included the implementation file
#endif
And in your implementation file (.cpp) you will need to use Header guards. I know, you normally use header guards in (.h) files, but this time you will also need to use them in your implementation file. See the snippet below:
#ifndef Stack_cpp //Header guards
#define Stack_cpp
#include "Stack.hpp"
template <typename T>
Stack<T>::Stack(){} //constructor
template <typename T>
void Stack<T>::push(T const& uElement){
elements.push_back(uElement); //push element into vector
}
template <typename T>
T Stack<T>::top() const{
if (elements.empty()) {
std::cout<<"Come on dude, I have nothing more to give";
throw std::out_of_range("Stack<>::top():empty stack");
}
return elements.back(); //return copy of last element
}
#endif
Now you will be able to use your template in your main.c file:
#include "Stack.hpp"
int main(int argc, const char * argv[]) {
//create Stack class of type int
Stack<int> stackOfInts;
//push value 2
stackOfInts.push(2);
//get last value
int intValue=stackOfInts.top();
std::cout<<intValue<<std::endl;
return 0;
}
You can grab the Inclusion-Model code example from my github page.
I hope this tutorial helped. If you want, sign up and get monthly development tips.