In: Computer Science
CSI 1440
Lab 6
“Class Templates”
Templates
Templates in C++ is not a difficult idea. This lab is not intended to help you understand the details of how templates work. It is only intented to give students an opportunity to start developing code using templated classes.
Everyone in the class has had to come to grips with the usage of variables in general. With templates, you can think of the type of the variable being a variable itself. The programmer is just saying that when you want to instantiate an instance of this class you will need to tell the compiler which variable type the class needs to use. After understanding that, templates just become a syntactical change.
Developing a templated class is not difficult either. I think the syntax is the most difficult aspect. It's just pure ugly. So, I suggest that you approach the MyArray class as if it was the IntArray class. Yes, I'm suggesting that you write pushBack, popBack, getSize, isEmpty, operator[], and toString as if you were writing the IntArray class we developed in the Big 4 lab. After completing that implementation and testing for correctness, go back and change all necessary instances of int to the template variable.
Here's the MyArray class written as MyInt from the Big 4 lab.
// You won't be able to inherit from ContainerIfc at this point which means you may // have to test locally to begin with. class MyArray { private: int *data; int capacity, size; public: // You will need to write all function defintions outside of the class defintion MyArray(); }; MyArray::MyArray(){ this->capacity = 5; this->size = 0; this->data = new int[this->capacity]; }
Here's the MyArray class with the inheritance and templating in place.
template <class T> class MyArray : public ContainerIfc<T> { private: T *data; int capacity, size; public: MyArray(); }; // notice the T used in the name of the class not the name of the function template <class T> MyArray<T>::MyArray(){ this->capacity = 5; this->size = 0; this->data = new T[this->capacity]; }
MyArray Class
MyArray will inherit from ContainerIfc. ContainerIfc is an abstract classes which requires you to implement the functions in your MyArray. In addition to overriding the pure virtual functions, you will need to implement the big four because of the precense of dynamic memory in the class.
ContainerIfc.h
#include <string> using namespade std; class BADINDEX{}; template <class T> class ContainerIfc { public: virtual ContainerIfc<T>& pushBack(T) = 0; virtual ContainerIfc<T>& popBack(T &) = 0; // throws BADINDEX virtual int getSize() = 0; virtual bool isEmpty() = 0; virtual T& operator[](int) = 0; // throws BADINDEX virtual string toString() = 0; };
Memory Requirements
You are required to follow a familiar memory scheme. Your MyArray should start with enough space to store 5 base objects. It should increase by 5 base objects when needed. It should never use less than 1/4 of the capacity which should be corrected by decreasing by 5 if needed. And, it should never drop below 5 allocated integers.
toString
You are required to implement a toString function for your MyArray. toString is a function commonly used to pass classes around in a simple format. It is commonly used for logging or debugging. For example, you can print the current state of your object using the toString method before and after a suspicious line of code to see changes made. Your toString function should print the object out using the following format:
[size] [capacity] [val1 val2 val3 ….]
toString example
6 10 1 22 4 54 9 45
Driver.cpp
#include <iostream>
#include "MyArray.h"
using namespace std;
template <class T>
void copyIt(MyArray<T> b) {
cout << b.toString() << endl;
}
int main() {
MyArray<int> a;
int val;
cout << "Testing default constructor..." <<
endl;
cout << "Size: " << a.getSize() << " should be 0"
<< endl;
cout << endl << "Testing toString..." <<
endl;
cout << a.toString();
cout << "Should be: \n0\n5\n" << endl;
cout << "Tesing pushBack()..." << endl;
for( int i = 0; i < 10; i++ ) {
a.pushBack(i);
}
cout << a.toString();
cout << "Should be: \n10\n10\n0\t1\t2\t3\t4\t5\t6\t7\t8\t9\n"
<< endl;
cout << "Testing copy constructor..." << endl;
copyIt(a);
cout << "Should be: \n10\n10\n0\t1\t2\t3\t4\t5\t6\t7\t8\t9\n"
<< endl;
cout << "Testing overloaded assignment operator..."
<< endl;
MyArray<int> b;
b = a;
cout << b.toString() << endl;
cout << "Should be: \n" << a.toString() <<
endl;
b = b;
cout << "Tesing popBack()..." << endl;
while( !a.isEmpty() ) {
a.popBack(val);
cout << val << "\t";
}
cout << endl;
cout << "Should be: \n9\t8\t7\t6\t5\t4\t3\t2\t1\t0" <<
endl;
cout << endl;
cout << "Update for overloaded assignment operator..."
<< endl;
cout << b.toString();
cout << "Should be different than: " << endl;
cout << a.toString();
cout << "Tesing operator[]..." << endl;
try {
a[2] = 6;
cout << "Should not see this message" << endl;
} catch (BADINDEX) {
cout << "Should see this message" << endl;
}
for( int i = 0; i < 6; i++ ) {
a.pushBack(i);
}
cout << "Starting with..." << endl << a.toString() << endl;
int x = 5;
for( int i = 0; i < 6; i ++) {
a[i] = x--;
}
cout << "Now is ..." << endl << a.toString()
<< endl;
cout << "Should be... " << endl;
cout << "6\n10\n5\t4\t3\t2\t1\t0\n";
return 0;
}
/*** ContainerIfc.h ***/
#include <string>
using namespace std;
class BADINDEX{};
template <class T>
class ContainerIfc {
public:
virtual ContainerIfc<T>& pushBack(T) = 0;
virtual ContainerIfc<T>& popBack(T &) = 0; // throws
BADINDEX
virtual int getSize() = 0;
virtual bool isEmpty() = 0;
virtual T& operator[](int) = 0; // throws BADINDEX
virtual string toString() = 0;
};
/*** MyArray.h ***/
#include "ContainerIfc.h"
template <class T>
class MyArray : public ContainerIfc<T> {
private:
T *data;
int capacity, size;
public:
MyArray();
ContainerIfc<T>& pushBack(T);
ContainerIfc<T>& popBack(T &); // throws
BADINDEX
int getSize();
bool isEmpty();
T& operator[](int); // throws BADINDEX
string toString();
};
// notice the T used in the name of the class not the name of
the function
template <class T>
MyArray<T>::MyArray(){
this->capacity = 5;
this->size = 0;
this->data = new T[this->capacity];
}
template <class T>
ContainerIfc<T>& MyArray<T>::pushBack(T item)
{
if(size==capacity)
{
capacity += 5;
T *temp = new T[this->capacity];
for(int i=0; i<size; i++)
temp[i] = data[i];
delete []data;
data = temp;
temp = nullptr;
}
data[size] = item;
size++;
return *this;
}
template <class T>
ContainerIfc<T>& MyArray<T>::popBack(T &item)
// throws BADINDEX
{
if(isEmpty())
throw BADINDEX();
item = data[size-1];
size--;
if(size==capacity-6 && capacity>5)
{
capacity -= 5;
T *temp = new T[this->capacity];
for(int i=0; i<size; i++)
temp[i] = data[i];
delete []data;
data = temp;
temp = nullptr;
}
return *this;
}
template <class T>
int MyArray<T>::getSize()
{
return this->size;
}
template <class T>
bool MyArray<T>::isEmpty()
{
return this->size==0;
}
template <class T>
T& MyArray<T>::operator[](int i) // throws BADINDEX
{
if(i<0 || i>=size)
throw BADINDEX();
return data[i];
}
template <class T>
string MyArray<T>::toString()
{
string s = "";
s = s + to_string(size) + "\n";
s = s + to_string(capacity) + "\n";
for(int i=0; i<size; i++)
{
s = s + to_string(data[i]) + "\t";
}
s = s + "\n";
return s;
}
/*** main.cpp ***/
#include <iostream>
#include "MyArray.h"
using namespace std;
template <class T>
void copyIt(MyArray<T> b) {
cout << b.toString() << endl;
}
int main() {
MyArray<int> a;
int val;
cout << "Testing default constructor..." <<
endl;
cout << "Size: " << a.getSize() << " should be 0"
<< endl;
cout << endl << "Testing toString..." <<
endl;
cout << a.toString();
cout << "Should be: \n0\n5\n" << endl;
cout << "Tesing pushBack()..." << endl;
for( int i = 0; i < 10; i++ ) {
a.pushBack(i);
}
cout << a.toString();
cout << "Should be: \n10\n10\n0\t1\t2\t3\t4\t5\t6\t7\t8\t9\n"
<< endl;
cout << "Testing copy constructor..." << endl;
copyIt(a);
cout << "Should be: \n10\n10\n0\t1\t2\t3\t4\t5\t6\t7\t8\t9\n"
<< endl;
cout << "Testing overloaded assignment operator..."
<< endl;
MyArray<int> b;
b = a;
cout << b.toString() << endl;
cout << "Should be: \n" << a.toString() <<
endl;
b = b;
cout << "Tesing popBack()..." << endl;
while( !a.isEmpty() ) {
a.popBack(val);
cout << val << "\t";
}
cout << endl;
cout << "Should be: \n9\t8\t7\t6\t5\t4\t3\t2\t1\t0" <<
endl;
cout << endl;
cout << "Update for overloaded assignment operator..."
<< endl;
cout << b.toString();
cout << "Should be different than: " << endl;
cout << a.toString();
cout << "Tesing operator[]..." << endl;
try {
a[2] = 6;
cout << "Should not see this message" << endl;
} catch (BADINDEX) {
cout << "Should see this message" << endl;
}
for( int i = 0; i < 6; i++ ) {
a.pushBack(i);
}
cout << "Starting with..." << endl << a.toString() << endl;
int x = 5;
for( int i = 0; i < 6; i ++) {
a[i] = x--;
}
cout << "Now is ..." << endl << a.toString()
<< endl;
cout << "Should be... " << endl;
cout << "6\n10\n5\t4\t3\t2\t1\t0\n";
return 0;
}
Output:
Testing default constructor...
Size: 0 should be 0
Testing toString...
0
5
Should be:
0
5
Tesing pushBack()...
10
10
0 1 2 3 4 5 6 7 8 9
Should be:
10
10
0 1 2 3 4 5 6 7 8 9
Testing copy constructor...
10
10
0 1 2 3 4 5 6 7 8 9
Should be:
10
10
0 1 2 3 4 5 6 7 8 9
Testing overloaded assignment operator...
10
10
0 1 2 3 4 5 6 7 8 9
Should be:
10
10
0 1 2 3 4 5 6 7 8 9
Tesing popBack()...
9 8 7 6 5 4 3 2 1 0
Should be:
9 8 7 6 5 4 3 2 1 0
Update for overloaded assignment operator...
10
10
8694400 8656464 808727609 909642032 909391413 808978740 926037816
154742838 909717561 858796596
Should be different than:
0
5
Tesing operator[]...
Should see this message
Starting with...
6
10
0 1 2 3 4 5
Now is ...
6
10
5 4 3 2 1 0
Should be...
6
10
5 4 3 2 1 0
Solving your question and
helping you to well understand it is my focus. So if you face any
difficulties regarding this please let me know through the
comments. I will try my best to assist you. However if you are
satisfied with the answer please don't forget to give your
feedback. Your feedback is very precious to us, so don't give
negative feedback without showing proper reason.
Always avoid copying from existing answers to avoid
plagiarism.
Thank you.