PROWAREtech

articles » current » c-plus-plus » template-classes

C++: Template Class Tutorial

How template classes work.

Templates are super simple and are a great way to reuse code.

First, two classes that do not use templates and do the same thing. These will be converted to use one class template.

#include <iostream>
#include <string>
using namespace std;

class Proware
{
private:
	int data;
	/* make this constructor private so that the class can not
	be initialized without passing the constructor an argument */
	Proware() {}

public:
	Proware(const int &argument) : data(argument) {}
	/* this method will double the value stored in the class
	and return it */
	int Double()
	{
		return data + data;
	}
};

int main()
{
	Proware obj(123);
	cout << obj.Double() << endl;
	return 0;
}
#include <iostream>
#include <string>
using namespace std;

class Proware
{
private:
	double data;
	Proware() {}

public:
	Proware(const double &argument) : data(argument) {}
	int Double()
	{
		return data + data;
	}
};

int main()
{
	Proware obj(123.123);
	cout << obj.Double() << endl;
	return 0;
}

Now, a small, over simplified example of how template classes work. To convert these classes, the class declaration is proceeded by template <class TGenericType> and then variables are declared as the generic type TGenericType.

#include <iostream>
#include <string>
using namespace std;

template <class TGenericType> class Proware
{
private:
	TGenericType data;
	Proware() {}

public:
	Proware(const TGenericType &argument) : data(argument) {}
	TGenericType Double()
	{
		return data + data;
	}
};

int main()
{
	Proware<int> obj(123);
	cout << obj.Double() << endl;
	return 0;
}

The method Double is defined inline. If defining a method (or member function) outside the class then it must be preceded with template <TGenericType>. The following syntax is also valid:

/* notice three TGenericTypes */
template <class TGenericType> TGenericType Proware<TGenericType>::Double()
{
	return data + data;
}

Here is this small example converted to a template class. It handles three datatypes with one class declaration.

#include <iostream>
#include <string>
using namespace std;

template <class TGenericType> class Proware
{
private:
	TGenericType data;
	Proware() {}

public:
	Proware(const TGenericType &argument);
	TGenericType Double();
};

template <class TGenericType> Proware<TGenericType>::Proware(const TGenericType &argument) : data(argument) {}

template <class TGenericType> TGenericType Proware<TGenericType>::Double()
{
	return data + data;
}

/* now the driver code; notice that the same class is
	 working with three datatypes (int, string and double) */
int main()
{
	// specify that the int datatype will be used
	Proware<int> obj_int(8);

	// specify that the string datatype will be used
	Proware<string> obj_string("Test");

	// specify that the float datatype will be used
	Proware<float> obj_float(8.8);

	/* double each of the values, notice how the string
			class is contatenating the values instead of adding */
	cout << obj_int.Double() << endl;
	cout << obj_string.Double() << endl;
	cout << obj_float.Double() << endl;
}

Template Specialization

Define a different implementation for a template (and not reuse code) with specialization. For example:

#include <iostream>
#include <string>
using namespace std;

template <class TGenericType> class Proware
{
private:
	TGenericType data;
	Proware() {}

public:
	Proware(const TGenericType &argument) : data(argument) {}
	TGenericType Double()
	{
		return data + data;
	}
};

template<> class Proware <string>
{
private:
	string data;
	Proware() {}

public:
	Proware(const string &argument) : data(argument) {}
	const string &Data() const
	{
		return data;
	}
	int Find(const string query) const
	{
		size_t found = data.find(query, 0);
		if (found == string::npos)
			return -1;
		return found;
	}
};

int main()
{
	Proware<string> obj_str("How now brown cow.");
	Proware<double> obj_dbl(1234.5678);
	Proware<int> obj_int(336699);

	cout << obj_str.Data() << endl;
	cout << "brown found at " << obj_str.Find("brown") << endl;
	cout << obj_dbl.Double() << endl;
	cout << obj_int.Double() << endl;
}

Templates can also have regular, as opposed to generic, typed parameters:

#include <iostream>
using namespace std;

template <class TGenericType, int number> class Proware
{
	TGenericType array[number];
public:
	void SetArrayItem(int index, TGenericType value);
	TGenericType GetArrayItem(int index);
};

template <class TGenericType, int index> void Proware<TGenericType, index>::SetArrayItem(int index, TGenericType value)
{
	array[index] = value;
}

template <class TGenericType, int index> TGenericType Proware<TGenericType, index>::GetArrayItem(int index)
{
	return array[index];
}

int main() {
	Proware<float, 10> floats;
	Proware<int, 10> ints;

	floats.SetArrayItem(2, 123.45);
	ints.SetArrayItem(5, 12345);

	cout << floats.GetArrayItem(2) << endl;
	cout << ints.GetArrayItem(5) << endl;

	return 0;
}

Template Functions

Functions can also have templates. This allows them to work with template classes but they stand on their own also.

template <typename TGenericType> TGenericType DoubleIt(TGenericType &data)
{
	return data + data;
}

PROWAREtech

Hello there! How can I help you today?
Ask any question

PROWAREtech

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
ACCEPT REJECT