Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class - User Defined Smart Array with dynamic size

I am writing the following array (class) that increases the size when index of this array is bigger than size of this array. I know about vectors but it has to be array. The Code looks like this:

#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 ok, 0 warnings , 0 valgrind errors, output :

3.14, 3.14, 3.14.

BUT i have to write this code ( in main ) this way :

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

and now it's 3 valgrind errors: Address (some_address) is 4 bytes inside a block of size 8 free'd,

and output looks like that : 0, 0, 3.14.

unfortunately i have to write this code to work the second way ( CustomArray[0] = CustomArray[1] = CustomArray[2] = 3.14; ) Can you guys help ? Thanks in advance

like image 567
Handsomeguy123 Avatar asked Oct 31 '22 23:10

Handsomeguy123


1 Answers

You need to resolve this through the use of a proxy type that holds a reference to the Array object and the index passed to your operator[]. This proxy type will be implicitly convertible to float and be assignable from float, making accesses (mostly1) transparent.

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

We need to make the following changes:

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

Change 1, inside of Array's definition:

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];
}

Change 2:

// Inside of Array
public:
    ArrayElement operator[](int index);

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

Change 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 declare ArrayElement before the definition of Array.)

See this working example.


1 One of the caveats of this approach is using type-inference (auto in C++11) on array accesses:

auto x = an_array[1];

Now x is an ArrayElement instead of a float and its value will be observed to change when an_array[1] changes. Attempting to assign a different float value to x will change the value in an_array[1] as well, since x is just a proxy to that value.

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

Note, however, that the std::vector<bool> specialization follows exactly the approach I've given here (returning a proxy object) and so it does have the same auto caveat! You could take that as a blessing on this approach.

like image 95
cdhowie Avatar answered Nov 13 '22 06:11

cdhowie