Reputation:
I am trying to implement a "Safe Array" class to practice operators overloading. I had successfully overloaded '[]' operator to return the right object reference.
One of the requirements of my safe array is that the pointers to the items will support pointers arithmetic, for example, given a pointer to arr[i]
I would be able to access arr[i+1]
by *(&(arr[i]) + 1)
. The safe array MUST protect this operation as well, and in case of trying to access an out-of-bounds object will throw an exception.
What is the best way to achieve this goal?
Upvotes: 0
Views: 461
Reputation: 597860
What you are asking for is a bit tricky, but doable by implementing some proxy objects, eg:
template<typename T>
class SafeArray
{
private:
T* m_arr;
size_t m_size;
T& at(size_t index);
const T& at(size_t index) const;
public:
...
class Proxy
{
private:
SafeArray<T>& m_sa;
size_t m_index;
Proxy(SafeArray<T>& sa, size_t index);
friend class SafeArray<T>;
public:
operator T() const;
Proxy& operator=(const T &value);
class Ptr
{
private:
SafeArray<T>& m_sa;
size_t m_index;
Ptr(SafeArray<T>& sa, size_t index);
friend class SafeArray<T>::Proxy;
public:
Ptr operator+(size_t rhs);
Ptr operator-(size_t rhs);
Ptr& operator++();
Ptr operator++(int);
Ptr& operator--();
Ptr operator--(int);
Proxy operator*();
};
Ptr operator&();
};
friend class Proxy;
Proxy operator[](size_t index);
};
template<typename T>
T& SafeArray<T>::at(size_t index)
{
if (index >= m_size)
throw std::out_of_range();
return m_arr[index];
}
template<typename T>
const T& SafeArray<T>::at(size_t index) const
{
if (index >= m_size)
throw std::out_of_range();
return m_arr[index];
}
template<typename T>
SafeArray<T>::Proxy SafeArray<T>::operator[](size_t index)
{
return Proxy(*this, index);
}
template<typename T>
SafeArray<T>::Proxy::Proxy(SafeArray<T>& sa, size_t index)
: m_sa(sa), m_index(index)
{
}
template<typename T>
SafeArray<T>::Proxy::operator T() const
{
return m_sa.at(m_index);
}
template<typename T>
SafeArray<T>::Proxy& SafeArray <T>::Proxy::operator=(const T &value)
{
m_sa.at(m_index) = value;
return *this;
}
template<typename T>
SafeArray<T>::Proxy::Ptr SafeArray<T>::Proxy::operator&()
{
return Ptr(m_sa, m_index);
}
template<typename T>
SafeArray<T>::Proxy::Ptr::Ptr(SafeArray<T>& sa, size_t index)
: m_sa(sa), m_index(index)
{
}
template<typename T>
SafeArray<T>::Proxy::Ptr SafeArray<T>::Proxy::Ptr::operator+(size_t rhs)
{
return Ptr(m_sa, m_index + rhs);
}
template<typename T>
SafeArray<T>::Proxy::Ptr SafeArray<T>::Proxy::Ptr::operator-(size_t rhs)
{
return Ptr(m_sa, m_index - rhs);
}
template<typename T>
SafeArray<T>::Proxy::Ptr& SafeArray<T>::Proxy::Ptr::operator++()
{
++m_index;
return *this;
}
template<typename T>
SafeArray<T>::Proxy::Ptr SafeArray<T>::Proxy::Ptr::operator++(int)
{
retrurn Ptr(m_sa, m_index++);
}
template<typename T>
SafeArray<T>::Proxy::Ptr& SafeArray<T>::Proxy::Ptr::operator--()
{
--m_index;
return *this;
}
template<typename T>
SafeArray<T>::Proxy::Ptr SafeArray<T>::Proxy::Ptr::operator--(int)
{
return Ptr(m_sa, m_index--);
}
template<typename T>
SafeArray<T>::Proxy SafeArray<T>::Proxy::Ptr::operator*()
{
return m_sa[m_index];
}
Upvotes: 1
Reputation: 560
You need to create iterators for your class. By default you should probably not ensure that &arr[i] + 1 is safe, because this would be hell to do (instead of returning the value, you would have to return some kind of special class holding the value that has some kind of overload for pointer access then overload for addition).
The way the STL handles safety is by letting you get an iterator by using a member such as arr.at( i ), which is a class that holds a pointer to whatever the datatype is, but also has some extra functionality that ensures some degree of safety in iterator manipulation.
class Array
{
struct Iterator
{
Iterator( Array* array, TYPE* ptr ) : m_ptr( ptr ) {}
...
Iterator operator +( int i ){
if( (m_ptr + i) < m_arr->end() )
return Iterator( m_arr, m_ptr + i );
}
};
...
Iterator at( unsigned int i ) {
if( i < m_size )
return Iterator( this, m_array + i );
}
}
Note that the iterator has a pointer to your original array class. This is necessary if you want your iterators to not go out of bounds like you asked, but the STL iterators do not work this way. Instead they make you check that iterator < array.end()
so that your iterators do not need to know about their creators.
Upvotes: 0