Skip to content

Instantly share code, notes, and snippets.

@rcktscnc
Created August 11, 2015 14:46
Show Gist options
  • Save rcktscnc/19990f51b9c14f292b75 to your computer and use it in GitHub Desktop.
Save rcktscnc/19990f51b9c14f292b75 to your computer and use it in GitHub Desktop.
/*
* this code was written for learning purposes
* 2015
*/
#ifndef CONTAINER_H
#define CONTAINER_H
#include <iostream>
template<typename T>
class Container
{
private:
T* object;
T OBJ_ERROR;
int array_size;
int array_iterator;
bool m_alloc(const int size);
bool removal_logic(const int remove_start, const int remove_end);
public:
Container<T>& operator= (const Container<T>& copy) noexcept;
T& operator[] (int object_location);
Container();
Container(const int size);
Container(const int size, const T& value);
Container(const Container<T>& copy) noexcept;
~Container();
bool add(const T& in_object);
bool add(const Container<T>& in_container);
T* addr(const int object_location);
int begin();
int capacity();
bool clear();
bool empty();
int end();
T get(const int object_location);
bool insert_single(const int object_location, const T& in_object);
bool insert_range(const int object_location, const Container<T>& in_container);
bool pop();
Container<T> range(const int start, const int end);
bool remove_single(const int object_location);
bool remove_range(const int start, const int end);
bool replace(const int object_location, const T& in_object);
bool shrink();
int size();
};
/* ****************************************************************************************************************************
* DEFINITION OF THE PRIVATE FUNCTIONS
* ****************************************************************************************************************************/
// m_alloc() generates a new memory block, copies the content to it, deletes the old block and points
// "object" to the new memory block. It always allocates roughly an extra 25% of memory, which prevents
// memory being allocated for each time an element is added. Which is nice.
template<typename T> bool Container<T>::m_alloc(const int size)
{
int temp_size = array_iterator + size;
array_size = (int)(temp_size + temp_size / 4);
T* n_mem_array = new T[array_size];
for (int i = 0; i < array_iterator; i++)
{
n_mem_array[i] = object[i];
}
delete[] object;
object = n_mem_array;
std::cout << "MEMORY ALLOCATION: " << array_size << "\n";
return true;
}
// removal_logic() handles the memory assignment logic of "remove" calls. It basically removes
// one or more elements and drop the gap that was created by the removal.
template<typename T> bool Container<T>::removal_logic(const int remove_start, const int remove_end)
{
if(remove_start <= remove_end && remove_end < array_iterator && remove_start >= 0)
{
T* n_mem_array = new T[array_size];
int remove_range = (remove_end - remove_start) + 1;
bool object_found = false;
for(int i = 0; i < array_iterator - remove_range; i++)
{
if(i < remove_start || i > remove_end)
{
if(object_found == false)
{
n_mem_array[i] = object[i];
}
else
{
n_mem_array[i] = object[i+remove_range];
}
}
else
{
n_mem_array[i] = object[i+remove_range];
object_found = true;
}
}
delete[] object;
object = n_mem_array;
array_iterator = array_iterator - remove_range;
std::cout << "OBJECTSSSSSS REMOVED!!!\n";
}
else
{
return false;
}
return true;
}
/* ****************************************************************************************************************************
* DEFINITION OF THE PUBLIC FUNCTIONS
* ****************************************************************************************************************************/
// operator= overload to copy other Containers. I wrote this with the "rule of three" in mind. I'm not sure
// it does any good, though. It is likely that the compiler would do a better job.
template<typename T> Container<T>& Container<T>::operator= (const Container<T>& copy) noexcept
{
if (object != nullptr)
{
delete[] object;
}
array_size = copy.array_size;
array_iterator = copy.array_iterator;
object = new T[array_size];
for (int i = 0; i < array_size; i++)
{
object[i] = copy.object[i];
}
std::cout << "ASSIGNMENT OPERATOR!\n";
return *this;
}
// operator[] overload to let users handle the Container as an array. I tried to limit the access to
// initialized-only indexes.
template<typename T> T& Container<T>::operator[] (int object_location)
{
if(object_location < array_iterator)
{
return object[object_location];
}
else
{
return OBJ_ERROR;
}
}
// default constructor.
template<typename T> Container<T>::Container()
{
array_size = 0;
array_iterator = 0;
object = nullptr;
std::cout << "DEFAULT CONSTRUCTOR\n";
}
// constructor with size initializer.
template<typename T> Container<T>::Container(const int size)
{
array_size = size;
array_iterator = 0;
object = new T[array_size];
std::cout << "REGULAR CONSTRUCTOR!\n";
}
// constructor with size and value initializers.
template<typename T> Container<T>::Container(const int size, const T& value)
{
array_size = size;
array_iterator = 0;
object = new T[array_size];
for (int i = 0; i < array_size; i++)
{
object[i] = value;
array_iterator++;
}
std::cout << "VALUE CONSTRUCTOR!\n";
}
// copy constructor. Note that I didn't use move semantics due to lack of knowledge and the will
// to not use any libraries. This constructor, as you should expect, is very similar to the operator=
// overload. I had the "rule of three" in mind when I wrote this function. The compiler could
// do a better job, though.
template<typename T> Container<T>::Container(const Container<T>& copy) noexcept
{
array_size = copy.array_size;
array_iterator = copy.array_iterator;
object = new T[array_size];
for (int i = 0; i < array_size; i++)
{
object[i] = copy.object[i];
}
std::cout << "COPY CONSTRUCTOR!\n";
}
// destructor.
template<typename T> Container<T>::~Container()
{
delete[] object;
std::cout << "DESTRUCTOR!\n";
}
// add() behaves like the vector's push_back() function. It adds an element to the end of the array.
// If the array is too short, than it allocates more memory by calling m_alloc() (see above).
template<typename T> bool Container<T>::add(const T& in_object)
{
if (array_iterator < array_size)
{
object[array_iterator] = in_object;
array_iterator++;
}
else
{
m_alloc(1);
object[array_iterator] = in_object;
array_iterator++;
}
return true;
}
// this version of add() adds the whole content of another Container to the end of the array. It can be
// used with the function range() like this container.add(container2.range(5, 10); (see below). If there is not
// enough space in the array, more memory
// is allocated.
template<typename T> bool Container<T>::add(const Container<T>& in_container)
{
if (in_container.object != nullptr)
{
if (object == nullptr)
{
array_size = in_container.array_iterator;
array_iterator = 0;
object = new T[array_size];
}
if (array_size - array_iterator >= in_container.array_iterator)
{
for (int i = 0; i < in_container.array_iterator; i++)
{
object[array_iterator] = in_container.object[i];
array_iterator++;
}
}
else
{
m_alloc(in_container.array_iterator);
for (int i = 0; i < in_container.array_iterator; i++)
{
object[array_iterator] = in_container.object[i];
array_iterator++;
}
}
}
else
{
/*
* this return is VERY important! Ignore this comment, by the way.
*/
std::cout << "ARGUMENT NOT INITIALIZED!!!!!!!!!\n";
return false;
}
return true;
}
// add() returns the address of one element. This function may be redundant considenring
// the operator[] overload basicaly does the same thing.
template<typename T> T* Container<T>::addr(const int object_location)
{
if (object_location < array_iterator && object != nullptr)
{
return &object[object_location];
}
else
{
return nullptr;
}
}
// Index of the first element. Interface porposes.
template<typename T> int Container<T>::begin()
{
return 0;
}
// current ammount of memory allocated.
template<typename T> int Container<T>::capacity()
{
return array_size;
}
// deletes the content of the Container.
template<typename T> bool Container<T>::clear()
{
if (object != nullptr)
{
delete[] object;
object = new T[array_size];
array_iterator = 0;
}
else
{
return false;
}
return true;
}
// checks if there is any element currently stored in the Container.
template<typename T> bool Container<T>::empty()
{
if (array_iterator > 0)
{
return false;
}
else
{
return true;
}
}
// index of the last element.
template<typename T> int Container<T>::end()
{
return array_iterator - 1;
}
// get() returns a copy of an element.
template<typename T> T Container<T>::get(const int object_location)
{
if (object_location < array_iterator && object != nullptr)
{
return object[object_location];
}
else
{
return OBJ_ERROR;
}
}
// insert_single() inserts an element anywhere in the array, pushing the other elements forward.
template<typename T> bool Container<T>::insert_single(const int object_location, const T& in_object)
{
if(object_location < array_iterator)
{
if(array_size <= array_iterator)
{
array_size++;
}
T* n_mem_array = new T[array_size];
bool insert_location_reached = false;
for (int i = 0; i <= array_iterator; i++)
{
if(i < object_location)
{
n_mem_array[i] = object[i];
}
else
{
if(insert_location_reached == false)
{
n_mem_array[i] = in_object;
insert_location_reached = true;
}
else
{
n_mem_array[i] = object[i-1];
}
}
}
delete[] object;
object = n_mem_array;
array_iterator++;
}
else
{
return false;
}
return true;
}
// insert_range() inserts a whole Container anywhere in the array, pushing the other elements forward.
template<typename T> bool Container<T>::insert_range(const int object_location, const Container<T>& in_container)
{
if(object_location < array_iterator)
{
if(array_size <= array_iterator + in_container.array_iterator)
{
array_size = array_size + in_container.array_iterator;
}
T* n_mem_array = new T[array_size];
bool insert_location_reached = false;
for (int i = 0; i <= array_iterator; i++)
{
if(i < object_location)
{
n_mem_array[i] = object[i];
}
else
{
if(insert_location_reached == false)
{
for(int k = 0; k < in_container.array_iterator; k++)
{
n_mem_array[i+k] = in_container.object[k];
}
insert_location_reached = true;
}
else
{
n_mem_array[(i+in_container.array_iterator)-1] = object[i-1];
}
}
}
delete[] object;
object = n_mem_array;
array_iterator = array_iterator + in_container.array_iterator;
}
else
{
return false;
}
return true;
}
// pop() deletes the value of the last index of the array. Actually, what it really does is make the last
// index available for overriding. The "popped" value will still be in memory until something
// else is added to the Container.
template<typename T> bool Container<T>::pop()
{
if(array_iterator > 0)
{
array_iterator--;
}
else{
return false;
}
return true;
}
// range() returns a new Container filtered by a any range. If you want the last 10 elements of a Container,
// you do this: Container<T> cont2 = cont1.range(cont1.end() - 10, cont1.end());
template<typename T> Container<T> Container<T>::range(const int start, const int end)
{
Container<T> filtered_container;
if (object != nullptr)
{
if (start < end && end < array_iterator)
{
filtered_container.array_size = (end - start) + 1;
filtered_container.array_iterator = 0;
filtered_container.object = new T[filtered_container.array_size];
for (int i = 0; i < filtered_container.array_size; i++)
{
filtered_container.object[i] = object[start + i];
filtered_container.array_iterator++;
}
}
}
return filtered_container;
}
// remove_single() removes an element from anywhere in the array, then drops the gap
// created by the removal.
template<typename T> bool Container<T>::remove_single(const int object_location)
{
return removal_logic(object_location, object_location);
}
// remove_range() removes a sequence of elements from anywhere in the array, then drops
// the gap created by the removal.
template<typename T> bool Container<T>::remove_range(const int start_location, const int end_location)
{
return removal_logic(start_location, end_location);
}
// replace() replaces any value in array. Could be redundant considering the operator[] overload.
template<typename T> bool Container<T>::replace(const int object_location, const T& in_object)
{
if(object_location < array_iterator)
{
object[object_location] = in_object;
}
else
{
return false;
}
return true;
}
// shrink() gets rid of all the excessive memory in the array. If you have 10 elements stored in a Container
// that has 30 slots, afther shrinking you'll have 10 elements and 10 memory slots.
template<typename T> bool Container<T>::shrink()
{
if (object != nullptr && array_size > array_iterator)
{
if (array_iterator > 0)
{
array_size = array_iterator;
T* n_mem_array = new T[array_size];
for (int i = 0; i < array_iterator; i++)
{
n_mem_array[i] = object[i];
}
delete[] object;
object = n_mem_array;
}
else
{
delete[] object;
object = nullptr;
array_size = 0;
}
}
else
{
return false;
}
return true;
}
// returns the current amount of elements stored in the Container.
template<typename T> int Container<T>::size()
{
return array_iterator;
}
#endif // CONTAINER_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment