This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I recently wrote an implementation of STL Vector as a programming exercise. The program compiles but I receive a strange error saying:
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
I've never come up with this error before and am not sure what exactly should be changed within my implementation to make it function correctly.
Can someone take a look through my code and see if anything jumps out at them as wrong in this specific case? Sorry I can't be more specific, I'm not sure where to look myself, thanks in advance.
#include <iostream>
#include <string>
#include <cassert>
#include <algorithm>
using namespace std;
template <class T>
class Vector
{
public:
typedef T * iterator;
Vector();
Vector(unsigned int size);
Vector(unsigned int size, const T & initial);
Vector(const Vector<T> & v);
~Vector();
unsigned int capacity() const;
unsigned int size() const;
bool empty() const;
iterator begin();
iterator end();
T & front();
T & back();
void push_back(const T & value);
void pop_back();
void reserve(unsigned int capacity);
void resize(unsigned int size);
T & operator[](unsigned int index);
Vector<T> & operator=(const Vector<T> &);
private:
unsigned int my_size;
unsigned int my_capacity;
T * buffer;
};
// Your code goes here ...
template<class T>
Vector<T>::Vector()
{
my_capacity = 0;
my_size = 0;
buffer = 0;
}
template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
my_size = v.my_size;
my_capacity = v.my_capacity;
buffer = new T[my_size];
for (int i = 0; i < my_size; i++)
buffer[i] = v.buffer[i];
}
template<class T>
Vector<T>::Vector(unsigned int size)
{
my_capacity = size;
my_size = size;
buffer = new T[size];
}
template<class T>
Vector<T>::Vector(unsigned int size, const T & initial)
{
my_size-size;
my_capacity = size;
buffer = new T [size];
for (int i = 0; i < size; i++)
buffer[i] = initial;
T();
}
template<class T>
Vector<T> & Vector<T>::operator = (const Vector<T> & v)
{
delete[ ] buffer;
my_size = v.my_size;
my_capacity = v.my_capacity;
buffer = new T [my_size];
for (int i = 0; i < my_size; i++)
buffer[i] = v.buffer[i];
return *this;
}
template<class T>
typename Vector<T>::iterator Vector<T>::begin()
{
return buffer;
}
template<class T>
typename Vector<T>::iterator Vector<T>::end()
{
return buffer + size();
}
template<class T>
T& Vector<T>::Vector<T>::front()
{
return buffer[0];
}
template<class T>
T& Vector<T>::Vector<T>::back()
{
return buffer[size - 1];
}
template<class T>
void Vector<T>::push_back(const T & v)
{
if (my_size >= my_capacity)
reserve(my_capacity +5);
buffer [my_size++] = v;
}
template<class T>
void Vector<T>::pop_back()
{
my_size--;
}
template<class T>
void Vector<T>::reserve(unsigned int capacity)
{
if(buffer == 0)
{
my_size = 0;
my_capacity = 0;
}
T * buffer = new T [capacity];
assert(buffer);
copy (buffer, buffer + my_size, buffer);
my_capacity = capacity;
delete[] buffer;
buffer = buffer;
}
template<class T>
unsigned int Vector<T>::size()const//
{
return my_size;
}
template<class T>
void Vector<T>::resize(unsigned int size)
{
reserve(size);
size = size;
}
template<class T>
T& Vector<T>::operator[](unsigned int index)
{
return buffer[index];
}
template<class T>
unsigned int Vector<T>::capacity()const
{
return my_capacity;
}
template<class T>
Vector<T>::~Vector()
{
delete[]buffer;
}
int main()
{
Vector<int> v;
v.reserve(2);
assert(v.capacity() == 2);
Vector<string> v1(2);
assert(v1.capacity() == 2);
assert(v1.size() == 2);
assert(v1[0] == "");
assert(v1[1] == "");
v1[0] = "hi";
assert(v1[0] == "hi");
Vector<int> v2(2, 7);
assert(v2[1] == 7);
Vector<int> v10(v2);
assert(v10[1] == 7);
Vector<string> v3(2, "hello");
assert(v3.size() == 2);
assert(v3.capacity() == 2);
assert(v3[0] == "hello");
assert(v3[1] == "hello");
v3.resize(1);
assert(v3.size() == 1);
assert(v3[0] == "hello");
Vector<string> v4 = v3;
assert(v4.size() == 1);
assert(v4[0] == v3[0]);
v3[0] = "test";
assert(v4[0] != v3[0]);
assert(v4[0] == "hello");
v3.pop_back();
assert(v3.size() == 0);
Vector<int> v5(7, 9);
Vector<int>::iterator it = v5.begin();
while (it != v5.end())
{
assert(*it == 9);
++it;
}
Vector<int> v6;
v6.push_back(100);
assert(v6.size() == 1);
assert(v6[0] == 100);
v6.push_back(101);
assert(v6.size() == 2);
assert(v6[0] == 100);
v6.push_back(101);
cout << "SUCCESS\n";
}
Here is the complete source code, updated from your source:
#pragma once
//using namespace std;
template <class T>
class Vector
{
public:
typedef T * iterator;
Vector();
Vector(unsigned int size);
Vector(unsigned int size, const T & initial);
Vector(const Vector<T> & v);
~Vector();
unsigned int capacity() const;
unsigned int size() const;
bool empty() const;
iterator begin();
iterator end();
T & front();
T & back();
void push_back(const T & value);
void pop_back();
void reserve(unsigned int capacity);
void resize(unsigned int size);
T & operator[](unsigned int index);
Vector<T> & operator=(const Vector<T> &);
void clear();
private:
unsigned int my_size;
unsigned int my_capacity;
T * buffer;
};
// Your code goes here ...
template<class T>
Vector<T>::Vector()
{
my_capacity = 0;
my_size = 0;
buffer = 0;
}
template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
my_size = v.my_size;
my_capacity = v.my_capacity;
buffer = new T[my_size];
for (unsigned int i = 0; i < my_size; i++)
buffer[i] = v.buffer[i];
}
template<class T>
Vector<T>::Vector(unsigned int size)
{
my_capacity = size;
my_size = size;
buffer = new T[size];
}
template<class T>
Vector<T>::Vector(unsigned int size, const T & initial)
{
my_size = size;
my_capacity = size;
buffer = new T [size];
for (unsigned int i = 0; i < size; i++)
buffer[i] = initial;
//T();
}
template<class T>
Vector<T> & Vector<T>::operator = (const Vector<T> & v)
{
delete[ ] buffer;
my_size = v.my_size;
my_capacity = v.my_capacity;
buffer = new T [my_size];
for (unsigned int i = 0; i < my_size; i++)
buffer[i] = v.buffer[i];
return *this;
}
template<class T>
typename Vector<T>::iterator Vector<T>::begin()
{
return buffer;
}
template<class T>
typename Vector<T>::iterator Vector<T>::end()
{
return buffer + size();
}
template<class T>
T& Vector<T>::front()
{
return buffer[0];
}
template<class T>
T& Vector<T>::back()
{
return buffer[my_size - 1];
}
template<class T>
void Vector<T>::push_back(const T & v)
{
if (my_size >= my_capacity)
reserve(my_capacity +5);
buffer [my_size++] = v;
}
template<class T>
void Vector<T>::pop_back()
{
my_size--;
}
template<class T>
void Vector<T>::reserve(unsigned int capacity)
{
if(buffer == 0)
{
my_size = 0;
my_capacity = 0;
}
T * Newbuffer = new T [capacity];
//assert(Newbuffer);
unsigned int l_Size = capacity < my_size ? capacity : my_size;
//copy (buffer, buffer + l_Size, Newbuffer);
for (unsigned int i = 0; i < l_Size; i++)
Newbuffer[i] = buffer[i];
my_capacity = capacity;
delete[] buffer;
buffer = Newbuffer;
}
template<class T>
unsigned int Vector<T>::size()const//
{
return my_size;
}
template<class T>
void Vector<T>::resize(unsigned int size)
{
reserve(size);
my_size = size;
}
template<class T>
T& Vector<T>::operator[](unsigned int index)
{
return buffer[index];
}
template<class T>
unsigned int Vector<T>::capacity()const
{
return my_capacity;
}
template<class T>
Vector<T>::~Vector()
{
delete[ ] buffer;
}
template <class T>
void Vector<T>::clear()
{
my_capacity = 0;
my_size = 0;
buffer = 0;
}
Maybe it's this typo?
Vector<T>::Vector(unsigned int size, const T & initial)
{
my_size-size;
Your "reserve" is broken. Use another variable name for the local buffer.
In addition to needing to fix your reserve function, your copy-constructor and copy-assignment-operator have an interesting problem:
Vector<T> t1 = t2;
This will set the capacity of t1 equal to the capacity (variable) of t2, but the actual capacity of t1 will be the size of t2; Thus, as you start pushing elements onto the vector after the copy-constructor/assignment-operator, you'll have a buffer overrun problem.
You need to change it to
template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
my_size = v.my_size;
my_capacity = v.my_capacity;
buffer = new T[my_capacity];
memcpy(buffer, v.buffer, my_size * sizeof(T));
}
OR (if you want to allow it to resize to a smaller array)
template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
my_size = v.my_size;
my_capacity = v.my_size;
buffer = new T[my_size];
memcpy(buffer, v.buffer, my_size * sizeof(T));
}
This code didn't compile for me. Clang complained that line 114 (the implementation of back() ) expected "size" to be called.
I think the line was intended to be "return buffer[size() -1];"
It also gives warnings about the implementation of this constructor:
template
Vector::Vector(unsigned int size, const T & initial)
The first line was probably supposed to be "my_size = size;"
The last line (of this constructor) should probably be deleted.
Next it fails the assertion at line 209: assert(v3.size() == 1);
This opens quite a can of worms, but the obvious problem is in resize() at the line: "size = size;" which is probably meant to be "my_size = size;"
With this change we now crash on line 121 which is in push_back() called from line 231 "v6.push_back(100);"
This is failing because of problems in reserve(). We are creating a local variable "buffer" with the same name as a member variable. Let's change the name to temp_buffer. Note: Do not assert() run time errors. assert() is for logic errors. This assert() cannot fail. new will never return 0. It will throw instead.
After making the obvious fixes in reserve() (there are other issues), we are now crashing in the copy() in reserve() in the call from resize() called from lin3 208 in main(), "v3.resize(1);".
We see that reserve is actually allocating a new buffer when we are reducing capacity. This is both a loss of performance and a loss of reliability (memory allocation can fail). But we should still not crash, so we'll try to prevent the crash without addressing the obvious design flaw.
The crash is coming because we are copying all of the items that exist in the container into the newly allocated array. This would be correct if we were only do this when we need to increase our capacity, but in this case, we have more items than our new capacity can hold. The code should set my_size to the new capacity if it was greater than that value.
Now the test code reports "SUCCESS."
However there are still many issues with this code.
One of the biggest issues is that we are not using uninitialized memory in the allocated array. Doing this is required by the standard for std::vector and it has both performance and reliability advantages. But it also complicates the code and so this may be a shortcut we can live with for what is obviously an intellectual exercise.
Constructors: Use initializer syntax to initialize data members.
With your copy constructor and your constructor from initial value, you'll leak the allocated array if any of your looped assignments throw an exception.
The assignment operator should allocate a new buffer of size "my_capacity" not "my_size" although there is an obvious optimization that if the size of the right-hand-side object is not greater than the "this" object, we shouldn't be allocating at all.
If allocating the new array fails in the assignment operator, we've already deleted the buffer, so we will eventually (when our Vector object is destroyed) have a double delete of the buffer and we may have all hell breaking loose before then.
In push_back(), in order to support the performance guarantees of the standard, we need to increase capacity by some constant fraction of the size of the existing capacity. For example something like: "reserve(my_capacity * 1.5);"
Related
I'm having problem understanding where my memory leak is located in my project.
The template I have built looks like this:
#pragma once
#include "IHeap.h"
#include <iostream>
using namespace std;
template <typename T>
class dHeap
{
public:
dHeap(T size);
dHeap(T size, int nr);
dHeap(const dHeap &original);
~dHeap();
dHeap<T>& operator=(const dHeap<T> &original);
void deepCopy(const dHeap &original);
void push(const T &item);
T pop();
T peek()const;
int size()const;
int getdValue()const;
void printAll()const;
void heapify(int arr[], int size, int root);
void heapSort(int arr[], int size);
private:
//T nr;
T *arrHeap;
T nrOfItems;
T capacity;
T dValue;
void expandHeap();
};
template<typename T>
inline dHeap<T>::dHeap(T size)
{
capacity = size;
arrHeap = new T[capacity + 1];
nrOfItems = 0;
dValue = size;
}
template<typename T>
inline dHeap<T>::dHeap(T size, int nr)
{
capacity = size;
arrHeap = new T[nr];
nrOfItems = 0;
dValue = size;
}
template<typename T>
inline dHeap<T>::dHeap(const dHeap &original)
{
this->deepCopy(original);
}
template<typename T>
inline dHeap<T>::~dHeap()
{
delete[] arrHeap;
}
template<typename T>
inline dHeap<T>& dHeap<T>::operator=(const dHeap<T>& original)
{
if (this != &original)
{
this->deepCopy(original);
}
return *this;
}
template<typename T>
inline void dHeap<T>::expandHeap()
{
capacity *= 2;
T *temp = new T[capacity];
for (int i = 0; i < nrOfItems; i++)
{
temp[i] = arrHeap[i];
}
delete[] arrHeap;
arrHeap = temp;
}
template<typename T>
inline void dHeap<T>::deepCopy(const dHeap &original)
{
capacity = original.capacity;
nrOfItems = original.nrOfItems;
arrHeap = new T[capacity];
dValue = original.dValue;
for (int i = 0; i < original.nrOfItems; i++)
{
this->arrHeap[i] = original.arrHeap[i];
}
}
template<typename T>
inline void dHeap<T>::push(const T &item)
{
if (nrOfItems >= capacity)
{
expandHeap();
}
arrHeap[nrOfItems] = item;
nrOfItems++;
}
template<typename T>
inline T dHeap<T>::pop()
{
int removed = arrHeap[0];
arrHeap[0] = arrHeap[nrOfItems - 1];
nrOfItems--;
return removed;
}
template<typename T>
inline T dHeap<T>::peek() const
{
return arrHeap[0];
}
template<typename T>
inline int dHeap<T>::size() const
{
return this->nrOfItems;
}
template<typename T>
inline int dHeap<T>::getdValue() const
{
return this->dValue;
}
template<typename T>
inline void dHeap<T>::printAll() const
{
for (int i = 0; i < nrOfItems; i++)
{
cout << "Heap element " << i << ". " << arrHeap[i] << endl;
}
}
template<typename T>
inline void dHeap<T>::heapSort(int arr[], int size)
{
for (int j = 0; j < size; j++)
{
// Build heap - which means rearrange array
for (int i = size / 2 - 1; i >= 0; i--)
{
heapify(arrHeap, size, i);
}
for (int i = size - 1; i >= 0; i--)
{
swap(arrHeap[0], arrHeap[i]);
heapify(arrHeap, i, 0);
}
//when re-structured heap, use pop and re-do it again until done
arr[j] = pop();
}
}
template<typename T>
inline void dHeap<T>::heapify(int arr[], int n, int root)
{
int largest = root;
int leftChild = 2 * root + 1;
int rightChild = 2 * root + 2;
// If left child is larger than root
if (leftChild < n && arr[leftChild] > arr[largest])
{
largest = leftChild;
}
// If right child is larger than largest so far
if (rightChild < n && arr[rightChild] > arr[largest])
{
largest = rightChild;
}
// If largest is not root, heapify recursivly until done
if (largest != root)
{
swap(arr[root], arr[largest]);
heapify(arr, n, largest);
}
}
I have a pointer called heapArr which I use to build up a heap. When the program terminates the destructor is called and there have I put a delete[] this->heapArr declaration to remove the pointer when program is done.
And I have also added a delete[] this->heapArr in the expand function in order to free the memory before allocation the new expanded array.
I'm not sure I explained this perfectly but the problem is that I seem to miss to remove something because I get a memory leak warning when I end the program.
What have I missed?
Memory leaks in deepCopy, where you allocate new memory without de-allocating the old.
That being said, don't allocate memory yourself. A good chunk of your code is duplicating the functionality of std::vector, so use std::vector<T> instead of T*.
(If for some reason you cannot use std::vector I would recommend you implement a replacement. Divide-and-conquer by splitting memory management from the heap logic.)
Just running through this example of a custom memory allocator header file and I was just wanting to understand some of the written code better.
class Allocator
{
public:
Allocator(size_t size, void* start)
{
_start = start;
_size = size;
_used_memory = 0;
_num_allocations = 0;
}
virtual ~Allocator()
{
ASSERT(_num_allocations == 0 && _used_memory == 0);
_start = nullptr;
_size = 0;
}
virtual void* allocate(size_t size, u8 alignment = 4) = 0;
virtual void deallocate(void* p) = 0;
void* getStart() const
{
return _start;
}
size_t getSize() const
{
return _size;
}
size_t getUsedMemory() const
{
return _used_memory;
}
size_t getNumAllocations() const
{
return _num_allocations;
}
protected:
void* _start;
size_t _size;
size_t _used_memory;
size_t _num_allocations;
};
namespace allocator
{
template <class T> T* allocateNew(Allocator& allocator)
{
return new (allocator.allocate(sizeof(T), __alignof(T))) T;
}
template <class T> T* allocateNew(Allocator& allocator, const T& t)
{
return new (allocator.allocate(sizeof(T), __alignof(T))) T(t);
}
template<class T> void deallocateDelete(Allocator& allocator, T& object)
{
object.~T();
allocator.deallocate(&object);
}
template<class T> T* allocateArray(Allocator& allocator, size_t length)
{
ASSERT(length != 0);
u8 headerSize = sizeof(size_t)/sizeof(T);
if(sizeof(size_t)%sizeof(T) > 0)
headerSize += 1;
//Allocate extra space to store array length in the bytes before the array
T* p = ( (T*) allocator.allocate(sizeof(T)*(length + headerSize), __alignof(T)) ) + headerSize;
*( ((size_t*)p) - 1 ) = length;
for(size_t i = 0; i < length; i++)
new (&p[i]) T;
return p;
}
template<class T> void deallocateArray(Allocator& allocator, T* array)
{
ASSERT(array != nullptr);
size_t length = *( ((size_t*)array) - 1 );
for(size_t i = 0; i < length; i++)
array[i].~T();
//Calculate how much extra memory was allocated to store the length before the array
u8 headerSize = sizeof(size_t)/sizeof(T);
if(sizeof(size_t)%sizeof(T) > 0)
headerSize += 1;
allocator.deallocate(array - headerSize);
}
};
Why is the start parameter needed/used here? If I wanted to allocate some space don't I just need to specify how much memory I need to allocate?
If I wanted to just create a simple linear custom memory allocater, would I still need to include the first two templates?
For this snippet new (&p[i]) T; why is the template type name at the end?
Thanks!
1) start stores the pointer to a "really" allocated memory that is being allocated by this allocator and later needs to be passed to corresponding free function.
2) i think it would be better to write your allocator from scratch
3) this expression is a placement new operator invocation and &p[i] is an address where an object of type T will be constructed
I have been looking through the std::vectors source code and looking at how it's capacity (vector.capacity()) function works, i'm not quite understanding how i would implement it into my Dynamic array source code. I would not just be returning the current container size would i. e.g dynarray.size().
,Thanks
#include <iostream>
#include <vector>
#include <iterator>
#pragma once
template<typename T>
class DynamicArrayIter
{
public:
DynamicArrayIter(T* data) : newData(data) { }
//DynamicArrayIter(const DynamicArrayIter& o); // Copy constructor
//DynamicArrayIter& operator=(const DynamicArrayIter& o); // Assignment operator
DynamicArrayIter operator++() { DynamicArrayIter i = *this;newData++; return i;}
DynamicArrayIter operator++(int junk) {newData++;return *this; }
T& operator*() {return *newData; }
bool operator==(const DynamicArrayIter& rhs) {return newData == rhs.newData; }
bool operator!=(const DynamicArrayIter& rhs) { return newData != rhs.newData;}
DynamicArrayIter<T> operator+(int _i)
{
DynamicArrayIter<T> iter = *this;
for (int i = 0; i < _i; ++i)
{
if (iter.newData) //If there's something to move onto...
++iter;
else
break;
}
return iter; //Return regardless of whether its valid...
}
private:
T* newData;
};
template<typename T>
class DynamicArray
{
public:
DynamicArray<T> operator=(const DynamicArray<T>&);//Dynamic Array equals Dynamic Array
DynamicArray();//Constructor
~DynamicArray();//Destructor
void push_back(const T&);//Push back a new element into the DynArray
void pop_back();//Pop an element off the back of the DynArray
void print();//Prints out what is in the container
bool empty();//Empty the DynArray container
void reserve(int);//Reserver a size of which the Dynarray can reach, once it reachers the limit it will increase etc.
void resize(int);//resize the Dynrray container data will be carried into the new size either cutting off eccess data or reserving space for more.
void swap(DynamicArray<T>);//Swap the contents in the Dynarray with another Dynarray containers.
void assign(size_t,T);//Assign new content to the Dynarray, replacing the current elements and changing its size accordingly
void assign(DynamicArrayIter<T>, DynamicArrayIter<T>);//Assign new content to the Dynarray, replacing the current elements and changing its size accordingly
void insert(DynamicArrayIter<T>,T);//Insert a element at a certain positon elements will be moved and adjusted accordingly
void erase(DynamicArrayIter<T>);//Erase an element at a certain postion
void erase(DynamicArrayIter<T>,DynamicArrayIter<T>);//Erase an element at a certain postion
T& at(int );// Element postion at index
T& front();//elements t postion
T& back();//elements back position
T& operator[] (int);//subscript location access
size_t capacity();//capacity of the container
size_t max_size();//max size of the vontainer
DynamicArrayIter<T> begin();//Begin on the container/DynArray - Iterator uses this to grab the begin of the Dynarray
DynamicArrayIter<T> end();//End on the container/DynArray - Iterator uses this to grab the End of the Dynarray
void clear();//Clear the whole container
int size();//Size of the current container returns sizeofarray
private:
T* myArray;//Where data is stored
int sizeofarray = 0;//size of the current container
};
template<typename T>
inline DynamicArray<T> DynamicArray<T>::operator=(const DynamicArray<T>&newDynArray)
{
myArray = new T[newDynArray.size()];
for (size_t i = 0; i < newDynArray.size(); i++)//will make the current array the size of the new one
{
myArray[i] = newDynArray.myArray[i];//Current Dynarray = the pass in Dynarray - Steps through changign each element
}
return newDynArray;//return the passed data
}
template<typename T>
inline DynamicArray<T>::DynamicArray()
{
myArray = new T[sizeofarray];//Creating a new Dynarray of size
}
template<typename T>
inline DynamicArray<T>::~DynamicArray()
{
delete[] myArray;//deleting the Dynarray
}
template<typename T>
inline void DynamicArray<T>::push_back(const T& pusheddata)
{
T *temp = myArray;//Creating a temp array with the value of the current Dynarray
myArray = new T[++sizeofarray];//Dynarray = new Dynarray of current size + 1 // Size is being incremented from this
myArray[sizeofarray - 1] = pusheddata;//Pushing the element onto the back of the Array
for (int i = 0; i < sizeofarray - 1; ++i)//It is sizearray - 1 as we dont the temp does not have the data we just pushed onto the back
{
myArray[i] = temp[i];//going through a loop putting the data from the temp we created back into the DynArray.
}
delete[] temp;//delete the temp
}
template<typename T>
inline void DynamicArray<T>::pop_back()
{
T *temp = myArray;//Creating a temp array with the value of the current Dynarray
myArray = new T[sizeofarray--];//Dynarray = new Dynarray of current size - 1 // Size is being decreased from this
for (int i = 0; i < sizeofarray; ++i)
{
myArray[i] = temp[i];//Dynarray equals the temp values
}
delete[] temp;//Delete the temp
}
template<typename T>
inline void DynamicArray<T>::print()
{
for (size_t i = 0; i < sizeofarray; i++)
{
std::cout << myArray[i] << std::endl;//Just looping through and printing the element until it hits size.
}
}
template<typename T>
inline bool DynamicArray<T>::empty()
{
if (size() == 0)
{
return true;//return true if size is 0
}
return false;//return flase if size >=1
}
template<typename T>
inline void DynamicArray<T>::reserve(int r_size)
{
sizeofarray = r_size;//size = the reserve size
}
template<typename T>
inline void DynamicArray<T>::resize(int newsize)
{
T *temp = myArray;//Creating a temp with the current Dynarray inside of it
myArray = new T[newsize];//Dynarray = a new Dynarray of size (newsize)
for (int i = 0; i < newsize; ++i)
{
myArray[i] = temp[i];//Setting the Dynarrays elements to the temps
}
for (int i = sizeofarray; i < newsize; i++)
{
myArray[i] = NULL;//Set the elements outside the size allowed to NULL
}
sizeofarray = newsize;//Size = new size
delete[] temp;//delete the temp
}
template<typename T>
inline void DynamicArray<T>::swap(DynamicArray<T> newSwap)
{
clear();//clear the current Dynarray
for (size_t i = 0; i < newSwap.sizeofarray; i++)
{
myArray[i] = newSwap.myArray[i];//Newly cleared Dynarray elements = passed in swapped data
sizeofarray++;//increment the size
}
}
template<typename T>
inline void DynamicArray<T>::assign(size_t n, T val)
{
clear();//Clear the Dynarray
myArray = new T[n];//Dynarray = new Dynarray of size_t n
for (size_t i = 0; i < n; i++)//for i < size_t n
{
myArray[i] = val;//Dynarray = val passed through
sizeofarray++;//increment the size of the Dynarray
}
}
template<typename T>
inline void DynamicArray<T>::assign(DynamicArrayIter<T> first, DynamicArrayIter<T> last)
{
int n = 0;//temp size holder
for (DynamicArrayIter<T> iter = first; iter != last; ++iter) {
n++;//increment the temp size holder
}
clear();//clear the Dynarray
myArray = new T[n];//Make a new Dynarray and its size is the temp size holders
for (DynamicArrayIter<T> newiter = first; newiter != last; ++newiter) {
myArray[sizeofarray] = *newiter;//Iterate through and set each element to the value passed in
sizeofarray++;//incremenet the size
}
}
template<typename T>
inline void DynamicArray<T>::insert(DynamicArrayIter<T> position, T val)
{
int sizeofthis = 0;//temp size holder for iter
int j = 0;//Index position // increments when position is meet
for (DynamicArrayIter<int> iter = begin(); iter != position; ++iter){
++sizeofthis;//increase the temp size holder fo riter
}
T *temp = myArray;//Create a new temp Dynarray
sizeofarray += 1;//temp size hodler + 1
myArray = new T[sizeofarray];//Dynarray = new Dynarray of temp size holder for iter
for (size_t i = 0; i < sizeofarray; i++)
{
if (i == sizeofthis)//if the for loops i = tempsize holders
{
myArray[sizeofthis] = val;//Dynarray element = val being passed in
j++;//Index pos ++
}
myArray[i + j] = temp[i];//Dynarray = Temps values // Will change when inserted pos is reached // dynamically chagne size
}
delete[] temp;//delete temp
}
template<typename T>
inline void DynamicArray<T>::erase(DynamicArrayIter<T> position)
{
int sizeofthis = 0;//temp size holder for iter
int j = 0;//index pos//increments wehn pos is met
for (DynamicArrayIter<int> iter = begin(); iter != position; ++iter) {
++sizeofthis;//increment the temp size holder
}
T *temp = myArray;//temp = current Dynarray
sizeofarray -= 1;//size decreased by 1
myArray = new T[sizeofarray];//new Dynarray of the new size
for (size_t i = 0; i < sizeofarray; i++)
{
if (i == sizeofthis)//if the loops i reaches the temp size holders value
{
myArray[sizeofthis] = myArray[sizeofthis + 1];//Dynarray at sizeoftihs = Dynarrays next element
j++;//index pos ++
}
myArray[i] = temp[i + j];//Dynarray = the temp[idexpos will be greater > 0 if i == sizeofthis]
}
delete[] temp;//delete the temp
}
template<typename T>
inline void DynamicArray<T>::erase(DynamicArrayIter<T> first, DynamicArrayIter<T> last)
{
int sizeofthis = 0;
for (DynamicArrayIter<int> iter = first; iter != last; ++iter) {
++sizeofthis;
}
T *temp = myArray;
sizeofarray = sizeofarray - sizeofthis - 1;
myArray = new T[sizeofarray];
for (size_t i = 0; i < sizeofarray; i++)
{
if (i < sizeofthis)
{
myArray[sizeofthis - 1 + i] =NULL;
}
myArray[i] = temp[i];
}
delete[] temp;
}
template<typename T>
inline T & DynamicArray<T>::at(int place)
{
return myArray[place];//return the element at place
}
template<typename T>
inline T & DynamicArray<T>::front()
{
return myArray[0];//return the first element in the array
}
template<typename T>
inline T & DynamicArray<T>::back()
{
return myArray[sizeofarray];//return the last element in the array
}
template<typename T>
inline T & DynamicArray<T>::operator[](int place)
{
return myArray[place];//return the element at place using subscript operator instead of dynarray.at()
}
template<typename T>
inline size_t DynamicArray<T>::capacity()
{
return back() - front();//
}
template<typename T>
inline size_t DynamicArray<T>::max_size()
{
return std::numeric_limits<T>::max();
}
template<typename T>
inline DynamicArrayIter<T> DynamicArray<T>::begin()
{
return DynamicArrayIter<T>(myArray);
}
template<typename T>
inline DynamicArrayIter<T> DynamicArray<T>::end()
{
return DynamicArrayIter<T>(myArray + sizeofarray - 1);
}
template<typename T>
inline void DynamicArray<T>::clear()
{
sizeofarray = 0;
myArray = new T[sizeofarray];
myArray[0] = NULL;
}
template<typename T>
inline int DynamicArray<T>::size()
{
return sizeofarray;
}
size() returns the number of elements that are held by the container while capacity is how many elements it can hold before more space must be allocated. So capacity can be greater than the size of the vector.
In your implementation, you have to create one more member capacity that will store the actual size of allocated array whereas size will hold the number of elements the container is holding.
I have a class called DiGraph, that contains an array to Node pointers:
DiGraph::DiGraph(int Size) : count(0){
nodes = new Node *[Size];
}
Now I want to define the function getNodes() which is essentially supposed to return the array in list form:
Liste<Node*> DiGraph::getNodes() {
Liste<Node*> nlist(count+1);
for (int i = 0; i < count; i++) {
nlist.append(nodes[i]);
}
return nlist;
}
At the end of the function the nlist is filled correctly but somehow the resulting copy at the function call does not contain the correct pointers but all other variables.
DiGraph a(6);
a.addNode(new Node("A", 50, 50));
Liste<Node*> gN = a.getNodes(); //gN does not contain the same pointers that were returned
The class 'Liste' is basically a dynamic array with templates and some fancy functions.
Now I think the answer is probably gonna be that nlist gets destroyed after the function ends but the pointers still remain valid in the nodes variable so why does the copy contain invalid ones?
EDIT
This is the list class:
#ifndef _LISTE_H
#define _LISTE_H
// -------------------------------------------------------------------
template <typename T>
class Liste {
private:
T *_values;
int _last;
int _size;
bool isFull();
int find(T value);
void increase();
void decrease();
public:
Liste(int size = 8);
Liste(Liste &list);
~Liste();
void append(T value);
void remove(T value);
T getValueAt(int pos);
int size();
T operator[](int pos);
};
// -------------------------------------------------------------------
template <typename T>
Liste<T>::Liste(int size) {
_size = size;
_last = 0;
_values = new T[size];
}
template <typename T>
Liste<T>::Liste(Liste &list) {
_size = list._size;
_last = list._last;
_values = list._values;
}
// -------------------------------------------------------------------
template <typename T>
Liste<T>::~Liste() {
delete[] _values;
}
// -------------------------------------------------------------------
template <typename T>
void Liste<T>::increase() {
T *tmp = new T[_size * 2];
for (int i = 0; i < _size; i++)
tmp[i] = _values[i];
delete[] _values;
_values = tmp;
_size *= 2;
}
// -------------------------------------------------------------------
template <typename T>
void Liste<T>::decrease() {
_size /= 2;
T *tmp = new T[_size];
for (int i = 0; i < _size; i++)
tmp[i] = _values[i];
delete[] _values;
_values = tmp;
}
// -------------------------------------------------------------------
template <typename T>
bool Liste<T>::isFull() {
return _last == _size;
}
// -------------------------------------------------------------------
template <typename T>
int Liste<T>::find(T val) {
int pos;
for (pos = 0; pos < _last; pos++)
if (_values[pos] == val)
return pos;
return -1;
}
// -------------------------------------------------------------------
template <typename T>
T Liste<T>::getValueAt(int pos) {
if (pos < 0 || pos >= _last)
throw "OutOfBoundsException";
return _values[pos];
}
// -------------------------------------------------------------------
template <typename T>
void Liste<T>::append(T val) {
if (isFull())
increase();
_values[_last] = val;
_last += 1;
}
// -------------------------------------------------------------------
template <typename T>
void Liste<T>::remove(T val) {
int pos = find(val);
if (pos == -1)
throw "ValueNotFoundException";
for (; pos < _last - 1; pos++)
_values[pos] = _values[pos + 1];
_last -= 1;
if (_last < _size / 4)
decrease();
}
// -------------------------------------------------------------------
template <typename T>
int Liste<T>::size() {
return _last;
}
// -------------------------------------------------------------------
template <typename T>
T Liste<T>::operator[](int pos) {
return getValueAt(pos);
}
#endif
template <typename T>
Liste<T>::Liste(Liste &list) {
_size = list._size;
_last = list._last;
_values = list._values;
}
What this code does is make the new Liste object (constructed from the copy constructor) point to the memory address of an existing Liste object. But this object will be destroyed so you get a dangling pointer. You need to perform a hard copy of the values.
template <typename T>
Liste<T>::Liste(const Liste &list) { // <--- const
_size = list._size;
_last = list._last;
_values = new T[_size];
for( std::size_t iter = 0 ; iter < _size ; ++iter )
{
_values[iter] = list._values[iter];
}
}
It is good modern practice to wrap pointer members to a smart pointer (eg unique_ptr<>). This way you will never forget to delete everything and object hold is guaranteed to be cleaned up even in a case of an incomplete constructor (exception thrown).
If you plan to return-by-value you should research into how to make your Liste class move-aware
Your T operator[](int pos); could also return a contained item by reference to allow immediate modification of the object ( T& operator[](int pos); ) cause, as it is, it returns a copy of the object that lies at _values[pos] and is most likely not what you want. Similarly for your T getValueAt(int pos); public method.
Having an issue when trying to pass a const reference value within a range based for loop to a
function that takes a const reference value. Look at commments within the range based for loop to see what I'm talking about. It doesn't seem to be an issue with my implementation of 'unordered_vector' which is what I thought at first since it prints correctly outside the function. Why does this fail? It works if I pass by value, so I'm a bit confused here. Looks like it is referencing the reference which doesn't seem to be correct behaviour.
void println(const vec3& p)
{
std::cout << '<' << p.x << ',' << p.y << ',' << p.z << '>' << std::endl;
}
void printlnvalue(vec3 p)
{
std::cout << '<' << p.x << ',' << p.y << ',' << p.z << '>' << std::endl;
}
const int vector_length = 7;
int _tmain(int argc, _TCHAR* argv[])
{
unordered_vector<vec3> vectors(vector_length);
for (int i = 0; i < vector_length; ++i)
{
vectors.push_back(vec3(0.1f*i, 0.2f*i, 0.3f*i));
}
for (const auto& i : vectors)
{
//Prints correctly
printlnvalue(i);
//Function that does same thing but doesn't print correctly.
println(i);
}
return 0;
}
Output
<0,0,0>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.1,0.2,0.3>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.2,0.4,0.6>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.3,0.6,0.9>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.4,0.8,1.2>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.5,1,1.5>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.6,1.2,1.8>
<8.68805E-044,2.66467E-038,4.12969E-039>
unordered_vector declaration
template<typename T>
class unordered_vector
{
private:
T* m_data;
size_t m_size;
size_t m_capacity;
public:
typedef unordered_vector_iterator<T> iterator;
typedef unordered_vector_iterator<T> const const_iterator;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
size_t size(void) const;
size_t capacity(void) const;
bool empty(void) const;
void resize(size_t count);
void reserve(size_t count);
void push_back(const T& val);
const_iterator begin(void) const;
const_iterator end(void) const;
void shrink_to_fit(void);
void erase(size_t i);
void erase(const T& val);
void erase_all(const T& val);
T& operator[](size_t i);
T operator[](size_t i) const;
unordered_vector& operator=(const unordered_vector& copy);
unordered_vector& operator=(unordered_vector&& mov);
unordered_vector(void);
unordered_vector(size_t _size);
unordered_vector(unordered_vector& copy);
unordered_vector(unordered_vector&& mov);
~unordered_vector(void);
};
unordered_vector definition
template<typename T>
/*
Returns the number of active units within
the unordered_vector.
*/
size_t unordered_vector<T>::size(void) const
{
return m_size;
}
template<typename T>
/*
Returns the potential size of the vector
before it would have to resize.
*/
size_t unordered_vector<T>::capacity(void) const { return m_capacity; }
template<typename T>
/*
Returns true if no active units are within the vector. size() == 0
*/
bool unordered_vector<T>::empty(void) const
{
return m_size == 0;
}
template<typename T>
/*
This resizes the vector where anything between the parameter and
size is part of the capacity.
*/
void unordered_vector<T>::reserve(size_t count)
{
if (count > m_capacity)
{
//Set capacity to new value
m_capacity = count;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
if (m_data != nullptr)
{
//Copy data from previous buffer to new buffer
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
}
}
template<typename T>
/*
Will resize the vector but each of the units is
active if the buffer is increased. If the size
is reduced then items at the end are truncated.
*/
void unordered_vector<T>::resize(size_t count)
{
if (count > m_capacity)
{
//Set capacity to new value
m_capacity = count;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
if (m_data != nullptr)
{
//Copy data from previous buffer to new buffer
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
//Make default values at each location
for (; m_size < count; ++m_size)
{
}
}
else if (count < m_capacity)
{
if (count < m_size) m_size = count;
//Set capacity to new value
m_capacity = count;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
if (m_data != nullptr)
{
//Copy data from previous buffer to new buffer
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
}
}
template<typename T>
/*
Shrinks capacity so capacity() == size().
*/
void unordered_vector<T>::shrink_to_fit(void)
{
if (m_size != m_capacity && m_data != nullptr)
{
m_capacity = m_size;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
if (m_data != nullptr)
free(m_data);
//Set pointer to the new array
m_data = new_ptr;
}
}
template<typename T>
/*
Will copy a value into the next open space of the vector.
If there isn't room for an additional item it will resize
the vector to accomadate it.
*/
void unordered_vector<T>::push_back(const T& val)
{
//If we don't have any more room we need
//to resize the array
if (m_size == m_capacity)
{
m_capacity = m_capacity == 0 ? 8 : m_capacity * 2;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
if (m_data != nullptr)
{
//Copy preexisting data byte for byte
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
}
m_data[m_size++] = val;
}
template<typename T>
/*
Random accessor for writing to array.
*/
T& unordered_vector<T>::operator[](size_t i)
{
assert(i >= 0 && i < m_size);
return m_data[i];
}
template<typename T>
/*
Random accessor for reading from array.
*/
T unordered_vector<T>::operator[](size_t i) const
{
assert(i >= 0 && i < m_size);
return m_data[i];
}
template<typename T>
/*
Constant time erase. It reorders the vector
internally to allow this.
*/
void unordered_vector<T>::erase(size_t i)
{
assert(i >= 0 && i < m_size);
if (i == m_size - 1)
m_size -= 1;
else
{
m_data[i] = m_data[m_size - 1];
m_size -= 1;
}
}
template<typename T>
/*
Will remove the first value that matches
val if it exists. In the event no value is
found the request is ignored.
*/
void unordered_vector<T>::erase(const T& val)
{
for (size_t i = 0; i < m_size; ++i)
{
if (m_data[i] == val)
{
if (i == m_size - 1)
m_size -= 1;
else
{
m_data[i] = m_data[m_size - 1];
m_size -= 1;
}
break;
}
}
}
template<typename T>
/*
Will remove all values that match the parameter.
If no items match the parameter then the request
is ignored.
*/
void unordered_vector<T>::erase_all(const T& val)
{
for (size_t i = 0; i < m_size; ++i)
{
if (m_data[i] == val)
{
if (i == m_size - 1)
m_size -= 1;
else
{
m_data[i] = m_data[m_size - 1];
m_size -= 1;
//Haven't checked the back yet
//so we need to recheck i
i--;
}
}
}
}
template<typename T>
/*
Initializes the vector with 0 capacity.
*/
unordered_vector<T>::unordered_vector(void)
: m_data(nullptr), m_size(0), m_capacity(0) {}
template<typename T>
/*
Initializes the vector with a capacity() equal to
the parameter.
*/
unordered_vector<T>::unordered_vector(size_t count)
: m_data(nullptr), m_size(0), m_capacity(0)
{
reserve(count);
}
template<typename T>
/*
Will copy data from another unordered_vector.
*/
unordered_vector<T>::unordered_vector(unordered_vector<T>& vector)
{
if (vector.m_capacity > 0)
{
m_data = malloc(sizeof(T)*vector.m_capacity);
if (vector.m_size > 0)
memcpy(m_data, vector.m_data, sizeof(T)*vector.m_size);
}
m_capacity = vector.m_capacity;
m_size = vector.m_size;
}
template<typename T>
/*
Move constructor to effeciently transfer data between
a temporary and another unordered_vector.
*/
unordered_vector<T>::unordered_vector(unordered_vector<T>&& mov)
{
m_data = mov.m_data;
m_capacity = mov.m_capacity;
m_size = mov.m_size;
mov.m_data = nullptr;
}
template<typename T>
/*
Destructor that deallocates memory
if any was allocated. Will not deallocate
memory if move semantic was invoked.
*/
unordered_vector<T>::~unordered_vector(void)
{
if (m_data != nullptr)
{
free(m_data);
m_data = nullptr;
}
m_size = 0;
m_capacity = 0;
}
template<typename T>
unordered_vector<T>& unordered_vector<T>::operator=(const unordered_vector<T>& copy)
{
if (m_data != nullptr)
{
free(m_data);
m_data = nullptr;
}
if (copy.m_capacity > 0)
{
m_data = malloc(sizeof(T)*copy.capacity());
if (copy.m_size > 0)
memcpy(m_data, copy.m_data, sizeof(T)*copy.m_size)
}
m_capacity = copy.m_capacity;
m_size = copy.m_size;
return *this;
}
template<typename T>
unordered_vector<T>& unordered_vector<T>::operator=(unordered_vector<T>&& mov)
{
if (m_data != nullptr)
free(m_data);
m_data = mov.m_data;
m_capacity = mov.m_capacity;
m_size = mov.m_size;
mov.m_data = nullptr;
}
template<typename T>
unordered_vector_iterator<T> const unordered_vector<T>::begin(void) const
{
return unordered_vector_iterator<T>(this, 0);
}
template<typename T>
unordered_vector_iterator<T> const unordered_vector<T>::end(void) const
{
return unordered_vector_iterator<T>(this, m_size);
}
iterator declaration
template<typename T>
class unordered_vector_iterator
{
private:
const unordered_vector<T>* const m_container;
size_t m_index;
public:
unordered_vector_iterator& operator++(void);
unordered_vector_iterator& operator++(int);
unordered_vector_iterator& operator--(void);
unordered_vector_iterator& operator--(int);
T& operator*(void) const;
T& operator->(void) const;
bool operator==(const unordered_vector_iterator& iter) const;
bool operator!=(const unordered_vector_iterator& iter) const;
unordered_vector_iterator(const unordered_vector<T>* const container, size_t index)
: m_container(container), m_index(index) {}
};
iterator definition
template<typename T>
bool unordered_vector_iterator<T>::operator==(const unordered_vector_iterator<T>& iter) const
{
return iter.m_index == m_index && iter.m_container == m_container;
}
template<typename T>
bool unordered_vector_iterator<T>::operator!=(const unordered_vector_iterator<T>& iter) const
{
return iter.m_index != m_index || iter.m_container != m_container;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator++(void)
{
++m_index;
return *this;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator++(int)
{
++m_index;
return *this;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator--(void)
{
--m_index;
return *this;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator--(int)
{
--m_index;
return *this;
}
template<typename T>
T& unordered_vector_iterator<T>::operator*(void) const
{
return (*m_container)[m_index];
}
template<typename T>
T& unordered_vector_iterator<T>::operator->(void) const
{
return (*m_container)[m_index];
}
vec3 declaration
struct vec3
{
public:
float x, y, z;
//vector functions
void normalize(void);
float magnitude(void) const;
float magnitudeSq(void) const;
bool operator==(vec3 o) const;
bool operator!=(vec3 o) const;
//Constructors
vec3(float _x, float _y, float _z)
: x(_x), y(_y), z(_z) {}
vec3(float s)
: x(s), y(s), z(s) {}
vec3()
: x(0.f), y(0.f), z(0.f) {}
};
You might need to declare another operator in unordered_vector to return a const T&. That's how it's declared in the STL vector class:
typedef const_reference const T&;
const_reference operator[](size_t idx) const;
The issue with the code is that the const method does not return a reference value.
Also for the code to work it has to return a non-constant reference value for some reason. Not a great answer but it works flawlessly now by changing the const index operator to this.
template<typename T>
/*
Random accessor for reading from array.
*/
T& unordered_vector<T>::operator[](size_t i) const
{
assert(i >= 0 && i < m_size);
return m_data[i];
}