C++ Data Structures 1.1 Sequential: Dynamic Array
By: Mateo Terselich
Tags: Cpp, Programming, Arrays, Object Oriented, Data Structures

Arrays! the simplest and most common data structure. On this post, we are going to explore how to build an array, that is dynamically stored in the Heap with dynamic size, commonly called “vector” in C++ STL, or “ArrayList” in Java Oracle API . Our array will be internally able to increase and decrease its size, it will store any type of data using templates with overridden methods from a pure virtual class (interface), and finally, it will implement the concept of circular array and other C++11 features.

To begin, let’s define what an array is. An array is simply a list of sequential elements stored in order, this means, we can access to any element in the list with the position of the element or also called index. In Computer Science, counting begins from 0, if we want to access to the first element of the list we simply refer to index 0.

array of 14 elements

Let’s dig more details, the compiler!, when you create an array at compile time (also called early binding) in C++, you must declare the data type and size (also called length), this is due to the fact that the compiler needs to know before hand how many followed memory slots must be reserved for the array, and each slot must have the capacity to store one single instance of the data type, for instance, an array of characters and size 10, the compiler gets the size of character (2 bytes) with sizeof operator, and reserves memory amount required by multiplying 2 bytes per 10 ( 20 bytes).

memory varies every 16 bits sizeof(char) in hexadecimal

Then, how indexing works? why indexing is performed in O(1)? Actually, it’s pretty simple, the compiler reads the index given, multiplies it by the size of the data type that the array carries, and finally, adds the result to the memory location of the first item of the array. This is pretty straight forward, array at index 0 is just 0 + the memory address of the array, that’s why the first element is always index 0.

indexing in a array

Now, let’s put our hands on the code. The class array_list<S> will have a template parameter (int by default). I will implement all the virtual functions from the interface sequence (append, pop, pull, push, insert_at, is_empty, empty, get_at, size and the operator []) you can find the code on my GitHub account the repository (HEADER_FILE SOURCE_FILE).

class declaration

I will explain later what each virtual function implementation does with the array_list<S> class. This class definition will follow Rule of three C++ programming (destructor, copy constructor and copy assignment operator).

About the class, following the encapsulation concept, to create abstractions to the user, the class array_list have all data members private.

S * array_ : Pointer to our dynamic sized array of S types in the heap.

: Pointer to our dynamic sized array of S types in the heap. int capacity_ : Internal capacity of the array.

: Internal capacity of the array. int front_index : First element of the list or starting point of the list, (I will explain this later) 0 by default.

: First element of the list or starting point of the list, (I will explain this later) 0 by default. int length_: length of the list or total number of elements currently stored in the list.

Because we are allocating dynamic memory, we must free the memory allocated in the destructor, when the destructor is called array_ is deleted and set it to NULL as follows.

//destructor......ARRAY_LIST<TYPE>

~array_list(

{

delete [] array_; //free [capacity_]

array = nullptr; //pointer dangling

}

Let’s start with the constructors, our array will have a default size of 10, the first constructor takes a size_t(unsigned integer or only positive integer numbers form 0 to 4294967296) that defines the capacity of the list, the default value will be 10. The front_index is 0 and no elements are in the list yet, so the length_ is 0 too. These constructors are explicit, this means that the object is created only if we explicit call the constructor. see the source code.

1) array_list<int> a(); ---> CREATES AN ARRAY OF TEN INTEGERS 2) array_list<char> b(20); ---> CREATES AN ARRAY OF 20 CHARACTERS

The code is here:

explicit constructors

Additionally, array_list<S> class has two more constructor which take one value of data type S, the first one takes either a r value or l value, it creates a dynamically allocated array in the Heap of size 10 and adds the r value using std::move to the first element of the list, length_ is assigned to 1, given the fact our list has already one element, front_index to 0, indicating that the first element of the list resides at zero index of the array.

On the other hand, the other constructor passes one type S value to fill the array with it, and a size_t value that defines length_ and capacity_ of the class that have the same value in this case.

array_list<std::string> b("Red"); array_list<char> a('a', 5); ---> ['a', 'a', 'a', 'a', 'a']

Before our copy constructor, let’s talk about the concept of circular array. A circular array is simply a linear array that connects its end with its beginning, how is this performed? An example will gives us a better idea, a circular array of 10 elements, this means we have an array of 10 elements that when is traversed can jump from index 9 to 0, (10 % 10 =0) this is calculated with the arithmetic operation module (%), and to get an element at a given index, we have determine the remainder of the division of the index and 10 (size of the array), for instance:

>>> circular_array[0] = array[(13) % 8]

<<< array[0] = 'a'



>>> circular_array[10] = array[(10) % 8]

<<< array[2] = 'c'



>>> circular_array[7] = array[(7) % 8]

<<< array[7] = 'h'

See the following image:

circular array representation

We need to have control over our array giving the user simple usage and understanding of the dynamic array, so if the user wants the first element of the list we just return the first value as the following example in the public method get_at [], we have to manage indexing access by throwing exceptions when the user wants to access to garbage indexes in the array:

operator[] overloading, get_at has the same functionality

Let’s see some examples of get_at to understand everything better:

get_at ARRAY_LIST:

length_ 6

capacity_ 8

front_index 2 (front_index + index)% capacity_ 1) get_at 0

<<< array_list[0] = array_[(2 + 0) % 8]

<<< array_[2]

2) get_at 3

<<< array_list[3] = array_[(2 + 3) % 8]

<<< array_[5]

3) array at 2

<<< array_list[2] = array_[(2 + 2) % 8]

<<< array_[4]

We can see the array of the example above.

array_list that hast front_index of 2

The copy constructor and the move constructor: let’s start with… 1) The move constructor: it transfers the ownership to our array_ from the r value list passed in the method, and updates the capacity_ and length_, then the r_list array_ is assigned to NULL to avoid pointer dangling because we are working with raw pointers. Once the destructor of r_list is called(out of scope), it does not destroy the array that our array_ pointer is pointing to.

copy and move constructors

the copy constructor copies the array_ of the argument in another memory allocation and copies every element in array_, this copy introduces the concept of circular array, why every value in the array in order is assigned to “(front_index + i )% capacity_” position of the argument array_? this is very simple but can be confusing. The first element in the list is front_index and sometimes front_index is not 0 (I will explain this later on this post), this is done behind scenes and the user has no access to this front_index variable, for the user the first element is at index 0, second at index 1 and so on…, but for the class internally the first element is at front_index + 0, second at front_index + 1... this way, array_ copies every element reorganizing the array in normal way.

Copy constructor example

In the example, when we copy the first element of the list, we have to find the first element in the copy array_, and then copy every element increment i in the for loop.

The public methods append, pop, push and pull modifies the length of the array and sometimes the capacity, this array class increases and decreases its size internally so the user does not have to take care of this confusing process every time a new element is inserted or deleted, this process is done in O(n) because every element of the list must be copied to a new array.

The methods that insert values in the array are overloaded for two cases, when you have a defined l-value and when you want to add an element in the array passing it in the argument as a r-value. if you don’t understand move semantics, I recommend this link that explains pretty well this case in specific.

append( Y ): Append inserts a value at the end of the array, in the method we first check if length_ is more or equal than capacity_ , so that a resized of the array must be performed by calling the private function resize(I will explain this method at the end of the post), otherwise, we insert the new item at the index length_ of the array_.

add a value at the end of the list

array_[(front_index + length_) % capacity_] = Y //(new value)...

push( X ): This member function inserts a value at the beginning of array_, this is a bit complex to perform and understand. if front_index is 0, then the new value of front_index will be the capacity of the array - 1, else just decrease the index of front_index by 1, and then, the element at the new front_index position is assigned to the argument passed in the method.

add a value at the beginning of the list

array[front_index] = X //(new value)...

pop(): This method, instead of delete, makes inaccessible the last element of the list, it decreases “ length_ ” of “ array_list<S> ”, because we have control over the accessible elements in the list with get_at or the operator [], the value that we just popped was never deleted, we just decreased the size of the array_list and if a user tries to access to this value, get_at throws the exception out of bounds, if append(new data) is called, that value will be replaced by new data, very easy and simple. Now I decided to add this case, when the length decreases more than a fifth of the array_list’s current capacity, shrink the array capacity by half calling resize private method (for now don’t worry about what the resize private method does).

” of “ ”, because we have control over the accessible elements in the list with get_at or the operator [], the value that we just popped was never deleted, we just decreased the size of the and if a user tries to access to this value, get_at throws the exception out of bounds, if append(new data) is called, that value will be replaced by new data, very easy and simple. Now I decided to add this case, when the length decreases more than a fifth of the array_list’s current capacity, shrink the array capacity by half calling resize private method (for now don’t worry about what the resize private method does). pull(): Here we do the same than pop, but instead, we increase the value of front_index by 1 and if front_index is equals than capacity_ -1, then front_index is 0 now following the circular array concept.

pop and pull implementation

The member method is_empty returns a boolean, checking if length_ is equal to 0 and the member method empty() simply sets length_ to 0 and the front_index to 0 too, giving the illusion to the user that the array is empty.

We must define a copy assignment operator to follow the 3 rules of C++ programming with classes, in the method we always must first check self assignment operation by getting the address of this (current array_list object or left side of the operator object) and the address of the argument, if addresses are the same, just return current array_list object. Secondly, if the copy fits into our array copy elements ordering the array and update length_.

for (auto i {0}; i < copy.length_ ; ++i ) //copy values ordering

this->array_[i] = copy.array_[(front + i) % copy.capacity_];

if the copy does not fit, delete our array_ and create a new array with the same capacity of the copy, then copy elements organizing elements to have a front_index equals to 0.

Copy assignment operator

delete [] array_; // free memory

array = new Type[copy.capacity_]; // then copy elements....

In the move assignment operator, we just transfer ownership of array_, length_, capacity_ and front_index, then we assign to NULL r_list.array_ and avoid pointer dangling issues, when r_list goes out of scope, deletes r_list.array_ that points to NULL and not our *array_.