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
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:
operator[]
and make it private; it will only be used by the proxy type.operator[]
that returns a value of 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With