I wrote an unbound array, to practice some C++. But the popBack-method doesn’t work properly. I’m just going to paste in my code, I hope it’s not to much. Code is compiled with gcc (MacPorts gcc47 4.7.1_1) 4.7.1. I would appreciate some help. Thanks 🙂
unbound_array.hpp
#ifndef _unbound_array_h_
#define _unbound_array_h_
#include <iostream>
namespace datastructures
{
template <typename T>
class UnboundArray {
private:
int beta;
int alpha;
int w;
int n;
//T data[];
T *data;
auto reallocate(const int size) -> void;
public:
UnboundArray(): beta(2) {
alpha = 4;
w = 1;
n = 0;
data = new T[w];
}
// void pushBack(T& element);
auto pushBack(T element) -> void;
auto popBack() -> T;
auto operator [](const int& b) -> T&;
};
template <typename T>
auto UnboundArray<T>::pushBack(T element) -> void {
if (n == w) {
reallocate(beta * n);
}
data[n] = element;
n++;
}
template <typename T>
auto UnboundArray<T>::popBack() -> T {
n = (n == 0) ? 0 : (n - 1);
if ((alpha * n <= w) && n > 0) {
reallocate(beta * n);
}
return data[n];
}
template <typename T>
auto UnboundArray<T>::operator [](const int& b) -> T&{
return data[b];
}
template <typename T>
auto UnboundArray<T>::reallocate(const int size) -> void {
int idx = n;
w = size;
T *array = new T[w];
for (int i = 0; i < idx; ++i) array[i] = data[i];
delete[] data;
data = array;
std::cout << "Reallocation. #elements: " << (n + 1)
<< " new size of UnboundArray: " << w << std::endl;
}
} // datastructures
#endif
test.cpp
#include <iostream>
#include "unbound_array.hpp"
using namespace datastructures;
int main() {
int num = 25;
std::cout << "Test of datastructures" <<std::endl;
UnboundArray<int> a;
for (int i = 0; i < num; ++i) {
std::cout << "Adding "<< i <<" to UnboundArray." << std::endl;
a.pushBack(i);
}
for (int i = 0; i < num; ++i) {
std::cout << "array[" << i << "] = "<< a[i] << std::endl;
}
for (int i = 0; i < num; ++i) {
std::cout << "Popping " << a.popBack() << std::endl;
}
return 0;
}
output
Adding (with resizing) works fine, also does operator[]. Just popBack troubles me.
Popping 24
Popping 23
Popping 22
Popping 21
Popping 20
Popping 19
Popping 18
Popping 17
Popping 16
Popping 15
Popping 14
Popping 13
Popping 12
Popping 11
Popping 10
Popping 9
Reallocation. #elements: 9 new size of UnboundArray: 16
Popping 8
Popping 7
Popping 6
Popping 5
Reallocation. #elements: 5 new size of UnboundArray: 8
Popping 3
Popping 3
Reallocation. #elements: 3 new size of UnboundArray: 4
Popping 556531726
Reallocation. #elements: 2 new size of UnboundArray: 2
Popping -268435456
Popping 0
EDIT
I changed the code in reallocate.
template <typename T>
auto UnboundArray<T>::reallocate(const int size) -> void {
w = size;
T *array = new T[w];
for (int i = 0; i < w; ++i) array[i] = data[i];
delete[] data;
data = array;
std::cout << "Reallocation. #elements: " << (n + 1)
<< " new size of UnboundArray: " << w << std::endl;
}
it works now.
EDIT2
reallocate() can’t be called twice with beta * n, in popBack() it has to be w / beta.
popBack() now returns return data[--n]. The array is no allocated with malloc().
The functionality is there, I’m going to add some checks, but that’s pretty much it. Thank you.
template <typename T, size_t ALPHA, size_t BETA>
auto UnboundArray<T, ALPHA, BETA>::pushBack(T& element) -> void {
if (n == w) {
reallocate(BETA * n);
}
data[n++] = element;
}
template <typename T, size_t ALPHA, size_t BETA>
auto UnboundArray<T, ALPHA, BETA>::popBack() -> T {
if ((ALPHA * n <= w) && n > 0) {
reallocate(w / BETA);
}
T ret = data[--n];
data[n] = 0;
return ret;
}
template <typename T, size_t ALPHA, size_t BETA>
auto UnboundArray<T, ALPHA, BETA>::operator[](const int& b) -> T& {
return data[b];
}
template <typename T, size_t ALPHA, size_t BETA>
auto UnboundArray<T, ALPHA, BETA>::reallocate(const int size) -> void {
w = size;
T* array = (T*) malloc(sizeof(T) * w);
for (int i = 0; i < n; i++) array[i] = data[i];
free(data);
data = array;
std::cout << "Reallocation. #elements: " << n << " new max size of UnboundArray: " << w << std::endl;
}
Your
popBackdecreases the size of the array, possibly relocates, which does a copy with the shrinked size and then tries to read the last element of the old size (which was not copied). I would suggest you change your popBack to:Also I would throw an exception when popBack is called on an empty array.
After the OPs edit
The problem I mentioned is in some ways solved by the edit, but there are still flaws:
reallocateis always called withbeta * nso inside reallocatesize = 2 * n. As data will holdnelements it will end up reading twice as much data as is there. This could trigger access violations so you should consider your code as unsafe as long this is present. Instead the copy method should only read as much as is there (n elements). Of course this takes as granted that n always holds the correct number of elements in the array, so it must not be changed beforereallocateis called.