Ok, I wanted to go and try to create my own BASIC version of an std::vector, just for 100% practice of working with different parts of C++ (operating overloading, copy constructors, class templates). This is just the beginning of it that I whipped up really fast and I know I do still have a lot of functionality to add.
The main feature I implemented was bounds checking in the array.
Couple things:
*I'm not trying to make something that will rival std::vector
*This would NEVER be used in production code. I'd go straight std::vector
*I can see a lot of the functionality could be implemented using more of the standard library.
*Doesn't really use anything from C++11. Everything must be copied, theirs no move constructor. Only uses a copy constructor.
*inefficient. I realize I am doing a lot of copying right now.
*I realize their are a lot of areas that could be made shorter, easier to follow, by using functions from the standard library. Just don't know a lot about them, but may refactor implementation to take advantage of some of those.
Though how is it coming right in the basic form? Anything you see that I should look to avoid? Things that stick out to you?
Here is the class
DynamicArray
// Implentation of a DynamicArray
// essiently a baby to the std::vector container
#pragma once
#include <stdexcept>
// DynamicArray Supports construction with an intial size, automatic memory handling
// access to the size of the DynamicArray, indexing like a normal array, resizing as needed, and finally preform an index range check
// DynamicArray is a template class that holds pointers to the Type
template <typename Type>
class DynamicArray
{
private:
// how many elements are currently in the array
unsigned int size;
// how large is the array
// this is not how many elements, this is the capacity of the array of buffer
unsigned int capacity;
// internally hold the pointer to the Types of objects
Type* objects;
public:
// explicit constructor
// Takes in the initial size of the dynamic array, defaults to 0
explicit DynamicArray(unsigned int startSize = 0): size(startSize), capacity(startSize)
{
// dynamilly allocate enough memory for the array
objects = new Type[capacity];
}
// Copy constructor, allow a deep copy
DynamicArray(const DynamicArray& rhs) : objects(nullptr)
{
// calls the assignment operator and passes in the rhs
operator=(rhs);
}
// Destructor deallocates all memory allocated
~DynamicArray()
{
delete[] objects;
}
// Overload the [] operator to allow working with DynamicArrays as if they were just a "regular" array
// Mutator overload of []
Type& operator[] (unsigned int index)
{
// check to see if the index was larger than the size of the dynamic array
if(index >= Size() )
{
throw std::runtime_error("Error! Index out of range!");
}
// return the object at that location
return objects[index];
}
// Accessor overload of []
const Type& operator[] (unsigned int index) const
{
if(index >= Size())
{
throw std::runtime_error("Error! Index out of range!");
}
return objects[index];
}
// Copy Assignment Operator
const DynamicArray& operator= (const DynamicArray& rhs)
{
// alias test
if( this!= &rhs)
{
// we need to reclaim the old memory
delete[] objects;
size = rhs.Size();
capacity = rhs.Capacity();
// allocate enough memory
objects = new Type[Capacity()];
// copy all the objects over
for(unsigned int i = 0; i < Size(); ++i)
{
objects = rhs.objects;
}
}
// return a reference to itself
return *this;
}
// resizes the DynamicArray
// Automatically creates more memory if needed
void Resize(unsigned int newsize)
{
// check to see if we need to expand
if(newSize > capacity)
{
// again reserve more memory for us
Reserve(newSize * 2);
}
size = newSize;
}
// Automatically reserves memory for the objects
// This has nothing to do with the size of the DynamicArray
// Sets a new capacity for the DyanamicArray to use
void Reserve(unsigned int newCapacity)
{
// no reason to reserve memory if the newCapacity is less than our size
if(newCapacity < size)
{
return;
}
// before we reserve we need to save all of the old objects
Type* oldObjects = objects;
// how many objects do we need to copy?
// if the newCapacity is smaller than the size then we have to copy over the capacity amount
unsigned int numToCopy = newCapacity < size ? newCapacity : size;
// allocate enough memory of the capacity specified
objects = new Type[newCapacity];
// Copy all the objects over
for(unsigned int i = 0; i < numToCopy; i++)
{
objects = oldObjects;
}
// size of the dynamic array is how many we copied over
size = numToCopy;
capacity = newCapacity;
// no longer need the memory the old objects resided at
delete[] oldObjects;
}
// PushBack pushes a new object to the end of the array
// allocates more memory if needed
void PushBack(const Type& theObject)
{
// if their is no more room left
if(size == capacity)
{
// make room by calling the reserve function
// Reserves enough room by doubling the current capacity
Reserve(2 * capacity + 1);
}
// add the object to the array, inreatment the size of the array afterwards
objects[size++] = theObject;
}
// returns how many elements are in the DynamicArray
inline unsigned int Size() const
{
return size;
}
// returns the capacity of the DynamicArray
// Capacity is not always the size.
// This is how many elements a DynamicArray can hold before more memory gets allocated
inline int Capacity() const
{
return capacity;
}
};
Anything to look at or change before I attempt to try to add additional functionality?
Functionality I look to add next:
*Constructor that allows you set the default values
*C++11 Move Constructor
*Initializer list Constructor
*Pop Back-Delete the last element
*Clear-Does nothing to capacity, sets the size to 0
*At-Access the element at an index. Also preforms bounds check. Can't change the element
*Swap-Swap contents of another DynamicArray
*Better Exception/Error Handling-Get rid of of just throwing a std::runtime_error
*Iterators-Most the functionality you would find inside std::vector with iterators (iterator, const_iterator, begin(), end() )
Again, this isn't made to rival std::Vector or to be used in any production code, just some practice. Still would like some input on how it's started. Thanks for any comments!