A custom Vector and Matrix class in C++ and operator[] - c++

I have a vector class in C++ that relies on raw pointer. I dont use std::vector as I need to create vector objects from raw pointers for specifal cases. Here is very simple example of my class:
template <typename T>
class Vector
{ ...
private:
T * m_data; int m_size; bool dontFree; ...
public:
Vector(T *const ptr, int size) { m_data = ptr; m_size = size; dontFree = true; }
Vector(int size, T val) { ... dontFree = false; }
~Vector(): { if(!dontFree) delete [] m_data; }
T& operator[](const size_type index);
};
Similarly I have the matrix data type that also stores data in raw pointer and can use vector to support [][] as it is not allowed in C++, something like:
template<typename T>
class Matrix
{
private:
T * m_data; ...
public:
...
Vector<T>& operator[](const int rowIndex)
{
return Vector<T>(&m_data[rowSize * rowIndex], rowSize);
}
}
How could I do efficiently implement operator[] for matrix returing a Vector so that I can write code, something follow:
Matrix<int> m(5,5);
m[1][1] = 10;
int temp = m[1][2];
Please suggest considering the overhead of copy constructor etc.

Create a proxy class that overloads operator[] that you can give access to your matrix's array. Something like this:
template<typename T>
class Proxy
{
public:
Proxy(T * tp)
:rowStart(tp)
{}
T & operator[](const int columnIndex)
{
return rowStart[columnIndex];
}
private:
T * rowStart;
};
Then your Matrix class' operator[] can return one of these, like this:
Proxy<T> operator[](const int rowIndex)
{
return Proxy<T>(m_data + rowSize * rowIndex);
}
It's not complete of course, but it should get you started.

You should return vector by value to make your code correct. Also you can write a small proxy if your vector does a lot of work inside a copy constructor.
If you implement your operator[] as inline method (e.g. don't move implementation to cpp) then good compiler should optimize your code and eliminate unnecessary copying.
But if you are crazy about performance then you can return a raw pointer from the operator:
...
T* operator[](const int rowIndex)
{
return m_data + rowSize * rowIndex;
}
...
int temp = m[1][2];
But is a dangerous approach!

The recommendation when implementing multidimensional matrices is not to overload operator[], but rather overload operator() with multiple dimensions. There are a few reasons that you can read in the C++ FAQ lite
template <typename T>
class Matrix {
public:
typedef std::size_t size_type;
typedef T & reference;
typedef T const & const_reference;
const_reference operator()( size_type x, size_type y ) const;
reference operator()( size_type x, size_type y );
};

Related

overload multidimensional array operator for vector

hi i am trying to create matrix class and i want to assign like matrix[0][2]=3
i checked form and found a solution with array but i want to do with vector and could not understand why does not it works ?
template<class T>
class Matrix
{
public:
Matrix(int a, int b)
{
vector<vector<T> > vec( a , vector<T> (b, 0));
matrixData = vec;
}
class Array1D
{
public:
Array1D(vector<T> a):temp(a) {}
T& operator[](int a)
{
return temp[a];
}
vector<T> temp;
};
vector<vector<T> > matrixData;
Array1D operator[] (int a)
{
return Array1D(matrixData[a]);
}
};
int main()
{
Matrix<int> n(3,5);
n[0][2] = 123; //assign
cout<<n[0][2]; // wrong output getting 0
}
You have to change Array1D implementation to:
class Array1D
{
public:
Array1D(vector<T>& a):temp(a) {}
T& operator[](int a)
{
return temp[a];
}
vector<T>& temp;
};
without this, every time you call operator[] on matrix you access temporary vector. Hence, every call n[0] works on different vector. So any previous modifications cannot be saved, and you always see 0 as result.
With the above change, you access original vector of matrix by Array1D proxy class.
Demo
You are returning the wrong type from the Matrix::operator[]. You need to return a nested vector by reference, so that you can chain the next [] to it:
vector<T>& operator[] (int a)
{
return matrixData[a];
}
In fact, you don't need the internal Array1D class at all for this purpose, since vector already has an operator[], and you can remove it entirely.
Here's a working demo.

Automatically create iterator from class that implements operator[]

Suppose I have a class that implements operator[], e.g.:
class Array {
public:
Array(size_t s) : data(new int[s]) {}
~Array() { delete[] data; }
int& operator[](size_t index) {
return data[index];
}
private:
int* data;
};
Is there a way to create a random access iterator from the class without having to manually create the iterator class and all its methods? I could manually define the class as follows:
class ArrayIterator : public std::iterator<std::random_access_iterator_tag, int> {
public:
ArrayIterator(Array& a) : arr(a), index(0) {}
reference operator[](difference_type i) {
return arr[index + i];
}
ArrayIterator& operator+=(difference_type i) {
index += i;
return *this;
}
bool operator==(const ArrayIterator& rhs) {
return &arr == &rhs.arr && index == rhs.index;
}
// More methods here...
private:
Array& arr;
difference_type index;
};
But doing so is time consuming since there are so many methods to implement, and each iterator for a class with operator[] would have the exact same logic. It seems it would be possible for the compiler to do this automatically, so is there a way to avoid implementing the entire iterator?
Is there a way to create a random access iterator from the class without having to manually create the iterator class and all its methods?
The simplest way to create a random-access iterator is to just use a raw pointer, which satisfies all of the requirements of the RandomAccessIterator concept (the STL even provides a default template specialization of std::iterator_traits for raw pointers), eg:
class Array {
public:
Array(size_t s) : data(new int[s]), dataSize(s) {}
~Array() { delete[] data; }
int& operator[](size_t index) {
return data[index];
}
size_t size() const { return dataSize; }
int* begin() { return data; }
int* end() { return data+dataSize; }
const int* cbegin() const { return data; }
const int* cend() const { return data+dataSize; }
private:
int* data;
size_t dataSize;
};
Implementing the random access operator by using operator[] may work, but it can be very inefficient and that's why compilers dont do that automatically. Just imagine adding operator[] to a class like std::list, where "going to element i" may take up to i steps. Incrementing an iterator based on operator[] would then have complexity O(n), where n is the size of the list. However, users of random access iterators, expect a certain efficiency, typically O(1).

avoiding dangling reference in array subscription operator

How to avoid dangling reference in array subscription operator in some vector implementation below? If realloc changes the pointer then references previously obtained from operator[] are no longer valid. I cannot use new/delete for this. I have to use malloc/realloc/free.
template <class T>
class Vector
{
public:
typedef size_t size_type_;
...
T& operator[] (const size_type_);
void push_back (const T&);
...
private:
const size_type_ page_size_;
size_type_ size_;
size_type_ capacity_;
T* buffer_;
};
template<class T>
inline
T&
some_namespace::Vector<T>::operator[] (const size_type_ index)
{
ASSERT(index < size_);
return buffer_[index];
}
template<class T>
inline
void
some_namespace::Vector<T>::push_back(const T& val)
{
if (size_ >= capacity_)
{
capacity_ += page_size_;
T* temp = (T*)(realloc(buffer_, capacity_*sizeof(T)));
if (temp != NULL)
{
buffer_ = temp;
}
else
{
free(buffer_);
throw some_namespace::UnexpectedEvent();
}
}
buffer_[size_++] = val;
}
By the way, the source of dangling reference in the code was this:
v_.push_back(v_[id]);
where v_ is an instance of Vector. To guard against this the new push_back is:
template<class T>
inline
void
some_namespace::Vector<T>::push_back(const T& val)
{
if (size_ >= capacity_)
{
const T val_temp = val; // copy val since it may come from the buffer_
capacity_ += page_size_;
T* temp = (T*)(realloc(buffer_, capacity_*sizeof(T)));
if (temp != NULL)
{
buffer_ = temp;
}
else
{
free(buffer_);
throw some_namespace::UnexpectedEvent();
}
buffer_[size_++] = val_temp;
}
else
{
buffer_[size_++] = val;
}
}
There are basically three things you can do:
Accept it as is and document it. This is what the STL does and it's actually quite reasonable.
Don't return a reference, return a copy. Of course, this means you can't assign to the vector's element anymore (v[42] = "Hello"). In that case you want a single operator[] which is marked const.
Return a proxy object which stores a reference to the vector itself and the index. You add an assignment to the proxy from const T& to allow writing to the vector's element and you need to provide some way to access/read the value, most likely an implicit operator const T&. Since the proxy object asks the vector on each access for the current position of the element, this works even if the vector changed the location of those elements between calls. Just be careful with multi-threading.
Here's a sketch for the proxy, untested and incomplete:
template<typename T> class Vector
{
private:
// ...
// internal access to the elements
T& ref( const size_type_ i );
const T& ref( const size_type_ i ) const;
class Proxy
{
private:
// store a reference to a vector and the index
Vector& v_;
size_type_ i_;
// only the vector (our friend) is allowed to create a Proxy
Proxy( Vector& v, size_type_ i ) : v_(v), i_(i) {}
friend class Vector;
public:
// the user that receives a Proxy can write/assign values to it...
Proxy& operator=( const T& v ) { v_.ref(i_) = v; return *this; }
// ...or the Proxy can convert itself in a value for reading
operator const T&() const { return v_.ref(i_); }
};
// the Proxy needs access to the above internal element accessors
friend class Proxy;
public:
// and now we return a Proxy for v[i]
Proxy operator[]( const size_type_ i )
{
return Proxy( *this, i );
}
};
Note that the above is incomplete and the whole technique has some drawbacks. The most significant problem is that the Proxy "leaks" in the API and therefore some use cases are not met. You need to adapt the technique to your environment and see if it fits.

Vector containing a partial subset of another vector both pointing to the same memory

I have a matrix class that stores its data in a std::vector:
std::vector<double> mData(mRows*mCols);
The class has a method to extract a column from this matrix:
std::vector<double> matrix::getCol(const int &n) const
{
std::vector<double> Col(mRows);
for(int ii = 0; ii < mRows; ii++)
{
Col[ii] = mData[n*mRows + ii];
}
return Col;
}
I would like to have this method return a reference to a vector that is a subset of mData. Is something like this possible?
std::vector<double>& matrix::getCol(const int &n)
{
std::vector<double> Col(mRows);
&Col[0] = &mData[n*mRows];
return Col;
}
The reason that I am interested in this is that I would like to use this method in assignment:
matrix A(rows,cols);
std::vector<double> newCol(rows);
A.getCol(0) = newCol;
Another alternative is to write a array_ref class, which contains a pointer to data and size, but does not own the data. It would allow modifications to elements, but no inserts or erases. Then you could construct it to point at regular arrays, vectors, of subsets of either. This is actually a fairly common practice with strings having a string_ref class, that might refer to the contents of a std::string, or a char*, or a char[N]. It would be fairly simple, and requires virtually no changes to your existing matrix class.
//untested sample
template<class T>
struct array_ref {
typedef T value_type;
typedef T& reference;
typedef T* pointer;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef T* iterator;
typedef const T* const_iterator;
array_ref() : data(nullptr), len(0) {}
array_ref(T* data_, size_t len_) : ptr(data_), len(len_) {}
T& at(size_t index) {assert_range(index); return ptr[index];}
const T& at(size_t index) const {assert_range(index); return ptr[index];}
T* begin() {return ptr;}
const T* begin() const {return ptr;}
T* end() {return ptr+len;}
const T* end() const {return ptr+len;}
T* data() {return ptr;}
const T* data() const {return ptr;}
T& operator[](size_t index) {return ptr[index];}
const T& operator[](size_t index) const {return ptr[index];}
size_t size() const {return len;}
private:
void assert_range(size_t index) const
{if (index>=len) throw std::out_of_range("out of range");}
T* ptr;
size_t len;
};
One way is to store the matrix's data into a std::vector<std::vector<double> >. Then, the implementation of matrix::getCol() is straightforward.
class matrix {
public:
matrix(int row, int col)
: mData(col, std::vector<double>(row))
{
}
std::vector<double>& getCol(int n)
{
return mData[n];
}
private:
std::vector<std::vector<double> > mData;
};
matrix A(rows, cols);
std::vector<double> newCol(rows);
A.getCol(0) = newCol; // works fine
Another way is defining matrix::setCol() instead.

Determining Purpose of operator[] Usage

Let's say I have something like the following method in my container class:
Datatype& operator[](const unsigned int Index) // I know this should use size_t instead.
{
return *(BasePointer + Index); // Where BasePointer is the start of the array.
}
I'd like to implement some sort of bounds-checking for the MyInstance[Index] = Value usage so the container resizes automatically if the user tries to change a value outside its range. However, I want something else to happen if the user tries to access a value outside the container's range, e.g. MyVariable = MyInstance[Index]. How can I detect how operator[] is being used?
Sketch:
return a proxy object instead of the actual data entry. The proxy object then defines operator = to handle the assignment case, and an implicit conversion operator for the reading-out case.
template <typename T>
class AccessorProxy {
friend class Container<T>;
public:
AccessorProxy(Container<T>& data, unsigned index)
: data(data), index(index) { }
void operator =(T const& new_value) {
// Expand array.
}
operator const T&() const {
// Do bounds check.
return *(data.inner_array + index);
}
private:
AccessorProxy(const AccessorProxy& rhs)
: data(rhs.data), index(rhs.index) {}
AccessorProxy& operator=(const AccessorProxy&);
Container<T>& data;
unsigned index;
};
template <typename T>
class ConstAccessorProxy {
friend class Container<T>;
public:
ConstAccessorProxy(const Container<T>& data, unsigned index)
: data(data), index(index) { }
operator const T&() const {
// Do bounds check.
return *(data.inner_array + index);
}
private:
ConstAccessorProxy(const ConstAccessorProxy& rhs)
: data(rhs.data), index(rhs.index) {}
ConstAccessorProxy& operator=(const ConstAccessorProxy&);
const Container<T>& data;
unsigned index;
};
AccessorProxy<Datatype> operator[](const unsigned int Index)
{
return AccessorProxy<Datatype>(*this, Index);
}
ConstAccessorProxy<Datatype> operator[] const (const unsigned int Index)
{
return ConstAccessorProxy<Datatype>(*this, Index);
}
The accessor classes will likely need to be be friends of the container class.
Finding ways to avoid the code duplication is left as an exercise to the reader. :)
Use a dummy class type to represent expressions like MyInstance[Index] and delay figuring out what to do until that expression is used.
class MyContainer {
private:
class IndexExpr {
public:
// Get data from container:
operator const Datatype&() const;
// Expand container if necessary, then store data:
Datatype& operator=(const Datatype& value);
// Treat MyInstance[i] = MyInstance[j]; as expected:
Datatype& operator=(const IndexExpr& rhs)
{ return *this = static_cast<const Datatype&>(rhs); }
private:
IndexExpr(MyContainer& cont, unsigned int ind);
MyContainer& container_;
unsigned int index_;
friend class MyContainer;
};
public:
IndexExpr operator[](unsigned int Index)
{ return IndexExpr(*this, Index); }
// No IndexExpr needed when container is const:
const Datatype& operator[](unsigned int Index) const;
// ...
};
This is not a perfect answer to "how to detect", but, if the user is accessing the operator[] via a const instance, then throw an exception if the index is out of bounds.
i.e.
Datatype const& operator[]() const { .. // don't modify here, throw exception
However, if the user is accessing the instance via a non const instance, then by all means expand if the index is out of bounds (and within your acceptable ranges)
Datatype& operator[]() { .. // modify here
Basically, you are using the const attribute of the instance to determine what your semantics would be (as done in std::map - i.e. trying to call operator[] on a const instance of a map results in a compiler error - i.e. there is no const qualified operator[] for map, because the function is guaranteed to create a mapping if the key does not exist already.)