Class - Custom Intelligent Array with Dynamic Size

I am writing the following array (class), which increases the size when the index of this array is larger than the size of this array. I know about vectors, but it must be an array . The code is as follows:

#include <iostream> using namespace std; class Array { public: Array():_array(new float[0]), _size(0){}; ~Array() {delete[] _array;} friend ostream &operator<<(ostream&,const Array&); float& operator[] (int index) { if(index>=_size) { float* NewArray=new float[index+1]; for(int i=0;i<_size;++i) NewArray[i]=_array[i]; for(int i=_size;i<index+1;++i) NewArray[i]=0; delete[] _array; _array=NewArray; _size=index+1; } return _array[index]; } private: float *_array; // pointer to array int _size; // current size of array }; ostream &operator << ( ostream &out, const Array& obj) // overloading operator<< to easily print array { cout << "Array:\n\n"; for (int i=0;i<obj._size;++i) { cout << obj._array[i]; if(i+1!=obj._size) cout << ", "; } cout << ".\n"; return out; } int main() { Array CustomArray; CustomArray[2] = CustomArray[1] = CustomArray[0] = 3.14; // **here is the problem** cout << CustomArray << endl; } 

Everything is fine, 0 warnings, 0 valgrind errors, output:

 3.14, 3.14, 3.14. 

BUT I have to write this code (mostly) as follows:

 CustomArray[0] = CustomArray[1] = CustomArray[2] = 3.14; 

and now these are 3 valgrind errors: Address (some_address) - 4 bytes inside a block of size 8 free'd,

and the output is as follows: 0, 0, 3.14.

Unfortunately, I have to write this code to work in the second way ( CustomArray[0] = CustomArray[1] = CustomArray[2] = 3.14; ) Can you help? thanks in advance

+5
source share
2 answers

You need to solve this problem with a proxy type containing a reference to an Array object and the index passed to your operator[] . This type of proxy will be implicitly converted to float and can be assigned from float , making access (mostly 1 ) transparent.

We also break rule three in this case and implement the copy-assignment operator to assign the value of one element of the array to another so that foo[0] = foo[1] works properly.

We need to make the following changes:

  • Rename the existing operator[] and make it private; it will only be used by the proxy type.
  • Create a new operator[] that returns a proxy type value.
  • Enter the type of proxy.

Change 1, inside the definition of Array :

 friend class ArrayElement; // So that ArrayElement can use access() private: float& access(int index) { if(index>=_size) { float* NewArray=new float[index+1]; for(int i=0;i<_size;++i) NewArray[i]=_array[i]; for(int i=_size;i<index+1;++i) NewArray[i]=0; delete[] _array; _array=NewArray; _size=index+1; } return _array[index]; } 

Edit 2:

 // Inside of Array public: ArrayElement operator[](int index); // Implementation outside of Array ArrayElement Array::operator[](int index) { return ArrayElement(*this, index); } 

Edit 3:

 class ArrayElement { friend class Array; // So that Array can use our private constructor private: ArrayElement(Array & array, int index) : array(array), index(index) { } public: // Allows "foo[1] = 2" ArrayElement const & operator=(float v) const { array.access(index) = v; return *this; } // Violation of the rule of three, but it makes sense in this case. // Allows "foo[1] = foo[2]" ArrayElement const & operator=(ArrayElement const & other) const { array.access(index) = other; return *this; } // Allows "float x = foo[1]" operator float() const { return array.access(index); } private: Array & array; int index; }; 

(Minor final change, you need to forward the declaration of ArrayElement before defining Array .)

See this working example .


1 . One caveat to this approach is the use of output type ( auto in C ++ 11) when accessing an array:

 auto x = an_array[1]; 

Now x is an ArrayElement instead of a float , and its value will change when an_array[1] changes. Trying to set a different value float x will also change the value in an_array[1] , since x is just a proxy for this value.

Compare this to the general behavior of std::vector , where auto x = a_vector[0] will result in x being the type of the vector element, and therefore will contain an independent copy of the value stored in the vector.

Note, however, that the specialization std::vector<bool> exactly matches the approach given here (returns a proxy object), and therefore it has the same auto Caution ! You can take this as a blessing on this approach.

+2
source

Use std::vector , directly or indirectly. Your expression

I know about vectors, but it should be an array.

doesn't make sense. std::vector guaranteed to have continuous storage, which probably means "array". For instance v you can always use the expression &v[0] to get the base address of the array, and starting with C ++ 11, it will be easier to read v.data() . This means that you can use vector for any function call that requires a "C-style" array as a pointer and size, for example. qsort .

If you cannot do without automatic resizing in Array::operator [] , then create the class wrapper as you did, but use std::vector internally. It is much simpler and safer. In particular, your code has quadratic worst case performance, for example. the following will be very slow:

 Array CustomArray; for ( int i = 0; i < 1000000; ++i ) CustomArray[i] = i; 

std::vector designed for this problem.

The other problem you mentioned with reference to invalidity can be easily solved with std::deque instead, however deque does not have continuous storage. So with std::vector you still have to use a proxy server as described by cdhowie. However, I must admit, I do not quite understand why the syntax should be like this, or what is wrong with calling std::vector<float>::resize() manually.

0
source

All Articles