Alright, so I'm trying to create a Matrix class, and I really, really, want to be able to call elements by using brackets. In the style of mMatrix[x][y].
So I have a vector<vector<T>> member, and when overloading the [] operator, I return a reference to a vector<T> object.
template<class T>
class Matrix
{
private:
uint32_t DimensionHorizontal;
uint32_t DimensionVertical;
std::vector<std::vector<T>> matrix;
public:
Matrix()
{
DimensionHorizontal = 10;
DimensionVertical = 10;
}
std::vector<T>& operator[] (int index)
{
return matrix.[index];
}
Matrix(int x, int y)
{
DimensionHorizontal = x;
DimensionVertical = y;
}
};
This seems to be working because when I create a Matrix object, and try to add an element by doing Matrix[a][n] (using integers in this case), it compiles without issues. I later try to print out the value stored there with cout.
During runtime, I get the following error
Expression: vector subscript out of range on Line 1455 of the vector.
On line 1455:
_NODISCARD size_type capacity() const noexcept { // return current length of allocated storage
auto& _My_data = _Mypair._Myval2;
return static_cast<size_type>(_My_data._Myend - _My_data._Myfirst);
}
_NODISCARD _Ty& operator[](const size_type _Pos) noexcept { // strengthened
auto& _My_data = _Mypair._Myval2;
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(
_Pos < static_cast<size_type>(_My_data._Mylast - _My_data._Myfirst), "vector subscript out of range");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _My_data._Myfirst[_Pos];
}
I am sort of confused about why this is happening. I know I'm trying to access something out of bounds, or doing something otherwise illegal, but Matrix[] should return a vector, and I should be able to use [] again to access the element T (in this case int), any help would be appreciated.
EDIT:
This is how I use the class
int main()
{
Matrix<int> a(10, 10);
a[0][0] = 10;
std::cout << a[0][0];
return 0;
}
You need to resize the matrix in your constructor to match the size passed as arguments.
Matrix(int x, int y) : matrix(x)
{
for( auto& sub : matrix ) {
sub.resize(y);
}
DimensionHorizontal = x;
DimensionVertical = y;
}
Related
Consider the task of writing an indexable class which automatically synchronizes its state with some external data-store (e.g. a file). In order to do this the class would need to be made aware of changes to the indexed value which might occur. Unfortunately the usual approach to overloading operator[] does not allow for this, for example...
Type& operator[](int index)
{
assert(index >=0 && index < size);
return state[index];
}
I there any way to distinguish between a value being accessed and a value being modified?
Type a = myIndexable[2]; //Access
myIndexable[3] = a; //Modification
Both of these cases occur after the function has returned. Is there some other approach to overloading operator[] which would perhaps make more sense?
From the operator[] you can only really tell access.
Even if the external entity uses the non cost version this does not mean that a write will take place rather that it could take place.
As such What you need to do is return an object that can detect modification.
The best way to do this is to wrap the object with a class that overrides the operator=. This wrapper can then inform the store when the object has been updated. You would also want to override the operator Type (cast) so that a const version of the object can be retrieved for read accesses.
Then we could do something like this:
class WriteCheck;
class Store
{
public:
Type const& operator[](int index) const
{
return state[index];
}
WriteCheck operator[](int index);
void stateUpdate(int index)
{
// Called when a particular index has been updated.
}
// Stuff
};
class WriteCheck
{
Store& store;
Type& object;
int index;
public: WriteCheck(Store& s, Type& o, int i): store(s), object(o), index(i) {}
// When assignment is done assign
// Then inform the store.
WriteCheck& operator=(Type const& rhs)
{
object = rhs;
store.stateUpdate(index);
}
// Still allow the base object to be read
// From within this wrapper.
operator Type const&()
{
return object;
}
};
WriteCheck Store::operator[](int index)
{
return WriteCheck(*this, state[index], index);
}
An simpler alternative is:
Rather than provide the operator[] you provide a specific set method on the store object and only provide read access through the operator[]
You can have (the non-const) operator[] return a proxy object that keeps a reference or pointer to the container, and in which operator= signals the container of the update.
(The idea of using const vs non-const operator[] is a red herring... you may know that you've just given away non-const access to the object, but you don't know if that access is still being used for a read or a write, when that write completes, or have any mechanism for updating the container thereafter.)
Another elegant (IMHO) solution...
Actually it is based on the fact that the const overload is called only when used on const object.
Lets first create two [] overloads - as it is required, but using different locations:
Type& operator[](int index)
{
assert(index >=0 && index < size);
return stateWrite[index];
}
const Type& operator[](int index) const
{
assert(index >=0 && index < size);
return stateRead[index];
}
Now you should create a shadow reference of your object when you need to "read" it as follows:
const Indexable& myIndexableRead = myIndexable; // create the shadow
Type a = myIndexableRead[2]; //Access
myIndexable[3] = a; //Modification
Creating this shadow declaration does not actually create anything in the memory. It just creates another name for your object with "const" access. It is all resolved at the compilation stage (including usage of const overload) and does not affect anything in runtime - neither memory nor performance.
And the bottom line - it is much more elegant (IMHO) than creating any assignment proxies, etc. I must state that the statement "From the operator[] you can only really tell access" is incorrect. According to the C++ Standard, returning dynamically allocatted object or global variable by reference is ultimate way to allow its direct modification, including [] overload case.
Following code has been tested:
#include <iostream>
using namespace std;
class SafeIntArray {
int* numbers;
int size;
static const int externalValue = 50;
public:
SafeIntArray( unsigned int size = 20 ) {
this->size = size;
numbers = new int[size];
}
~SafeIntArray() {
delete[] numbers;
}
const int& operator[]( const unsigned int i ) const {
if ( i < size )
return numbers[i];
else
return externalValue;
}
int& operator[]( const unsigned int i ) {
if ( i < size )
return numbers[i];
else
return *numbers;
}
unsigned int getSize() { return size; }
};
int main() {
SafeIntArray arr;
const SafeIntArray& arr_0 = arr;
int size = arr.getSize();
for ( int i = 0; i <= size ; i++ )
arr[i] = i;
for ( int i = 0; i <= size ; i++ ) {
cout << arr_0[i] << ' ';
}
cout << endl;
return 0;
}
And the results are:
20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 50
Return a proxy object which will have:
operator=(Type const &) overloaded for writes
operator Type() for reads
in the access example you give you can get a distinction by using a const version:
const Type& operator [] ( int index ) const;
on a sidenote, using size_t as index gets rid of the need for checking if index >= 0
#include "stdafx.h"
#include <iostream>
template<typename T>
class MyVector
{
T* _Elem; // a pointer to the elements
int _Size; // the size
public:
// constructor
MyVector(int _size):_Size(_size), _Elem(new T[_size])
{
// Initialize the elemets
for( int i=0; i< _size; ++i )
_Elem[i] = 0.0;
}
// destructor to cleanup the mess
~MyVector(){ delete []_Elem; }
public:
// the size of MyVector
int Size() const
{
return _Size;
}
// overload subscript operator
T& operator[]( int i )
{
return _Elem[i];
}
};
int _tmain(int argc, _TCHAR* argv[])
{
MyVector<int> vec(10);
vec[0] =10;
vec[1] =20;
vec[2] =30;
vec[3] =40;
vec[4] =50;
std::cout<<"Print vector Element "<<std::endl;
for (int i = 0; i < vec.Size(); i++)
{
std::cout<<"Vec["<<i<<"] = "<<vec[i]<<std::endl;
}
return 0;
}
I am making a Pentago game for someone, and I wanted to write a good code, so I decided to use operator overloading.
We have 2 classes; first one is Block class (which represents every single block of the board) and the second one is Set class (which represents a 3*3 table of blocks).
Now I want to use Set as a 2d array so I can use set[foo][foo].
Can you help me to make an operator like this?
A very simple solution is
struct MyClass {
int x[3][3];
int* operator[](int row) { return &(x[row][0]); }
};
i.e. returning an element* from operator[].
This allows using
myinstance[row][col]
There are (at least) two ways to go here.
The first is to make something like a set_row class, which is a proxy. So you'd have something like
class set
{
public:
set_row operator[](size_t row)
{
// Return a proxy object that just sees the correct row.
return set_row(internal_buffer_pointer[row]);
}
...
};
where set_row is something like
class set_row
{
public:
// Ctor takes a row
// Take a column, and return a reference to the correct column in the row.
element &operator[](size_t column);
};
From experience (on, ahem, VisualC++), this was slow, as it will need to construct a proxy object for each access.
The second is to forgo operator[], and use operator():
class set
{
public:
element &operator()(size_t row, size_t col);
...
};
It would be nice using operator[], but, unfortunately, you can't do that with it.
There is no operator[][]. If you want to provide those semantics you need to overload operator[] such that it returns another object that also overloads operator[].
Your case can be solved using a vector of vectors:
#include <vector>
#include <cstdint>
#include <iostream>
struct Block
{
int value = 0;
};
class Set
{
std::vector<std::vector<Block> > grid;
public:
Set(): grid(3, std::vector<Block>(3)) {} // 3 x 3
std::vector<Block>& operator[](std::size_t x) { return grid[x]; }
};
int main()
{
using std::size_t;
Set set;
set[1][1].value = 1;
for(size_t x = 0; x < 3; ++x)
{
for(size_t y = 0; y < 3; ++y)
{
std::cout << set[x][y].value << ' ';
}
std::cout << '\n';
}
}
Output:
0 0 0
0 1 0
0 0 0
This works because Set::operator[] returns reference to a std::vector and the std::vector overloads operator[] to return a reference to a Block.
There is no way to supply an operator[][] for a class.
However, if your Set supplies an operator[](), that operator can return a reference to something else that also has an operator[]().
For example;
class Row
{
public:
Block &operator[](int block_no) {return data[block_no];};
private:
std::vector<Block> data;
};
class Set
{
public:
Row &operator[](int row_no) {return row[row_no];};
private:
std::vector<Row> row;
};
int main()
{
Set s;
// assume s is set up appropriately
Block b = s[2][3]; // equivalent to s.operator[](2).operator[](3)
}
Obviously, it is also necessary to do relevant error checking, set up the contents of the classes correctly, etc.
Assuming the memory is contiguous, you can return a pointer to the first element of the row.
Working example
#include <iostream>
class MyType
{
public:
static const size_t rows = 3;
static const size_t columns = 3;
static const size_t size = rows * columns;
MyType()
{
for(size_t index = 0; index < 9; ++index)
{
data[index] = index;
}
}
int* operator[](size_t index)
{
return &data[rows * index];
}
private:
int data[size];
};
int main()
{
MyType instance;
std::cout << instance[2][1] << std::endl;
}
I've been trying to create a variable length multidimensional array. As I understand, you can't create variable length arrays on the stack, but you can create 1D variable length arrays in C++ using dynamic allocation. Correct me if this is a compiler extension, but it seems to work fine on clang and gcc with --pedantic set.
int size = 10;
int *ary = new int[size]();
I tried to extend the concept to multidimensional arrays. Here are my results. The problem with possiblity 1 and 2 is that they require a constexpr and do not work with variable sizes. Is it possible to make either of them accept a variable as its size? I put possibility 3 as I am aware of it, but it lacks [][] access, which is what I'm looking for.
constexpr int constSize = 10;
//Possibility 1: Only works in C++11
//Creates CONTIGUOUS 2D array equivalent to array[n*n], but with [][] access
int (*ary1)[constSize] = new int[constSize][constSize]();
delete [] ary1;
//Possibility 2:
//Really horrible as it does NOT create a contiguous 2D array
//Instead creates n seperate arrays that are each themselves contiguous
//Also requires a lot of deletes, quite messy
int **ary2 = new int*[constSize];
for (int i = 0; i < n; ++i)
ary2[i] = new int[constSize];
for (int i = 0; i < n; ++i)
delete [] ary2;
delete [] ary2;
//Possibility 3:
//This DOES work with non-constexpr variable
//However it does not offer [][] access, need to access element using ary[i*n+j]
int *ary3 = new int[size*size];
delete [] ary3;
This will create a dynamically allocated 2D variable-length array, with dimensions w and h:
std::vector<std::vector<int>> ary4(w, std::vector<int>(h));
It can be accessed with [][]:
ary4[x][y] = 0;
However, it's not contiguously allocated. To get a contiguous array, here's one solution:
template<typename E>
class Contiguous2DArray
{
public:
Contiguous2DArray(std::size_t width, std::size_t height)
: array(width * height), width(width) {}
E& operator()(std::size_t x, std::size_t y)
{ return array[x + width * y]; }
private:
std::vector<E> array;
std::size_t width;
}
It can be used like this:
Contiguous2DArray<int> ary5(w, h);
ary5(x, y) = 0;
The number of dimensions is fixed, because the type of what [] returns changes based on the number of dimensions. Access is through both repeated [] and (...). The first mimics C-style array lookup. The (...) syntax must be complete (it must pass N args to an N dimensional array). There is a modest efficiency cost to support both.
Uses C++14 features to save on verbosity. None are essential.
Using an an n_dim_array with 0 dimensions will give bad results.
template<class T, size_t N>
struct array_index {
size_t const* dimensions;
size_t offset;
T* buffer;
array_index<T,N-1> operator[](size_t i)&&{
return {dimensions+1, (offset+i)* *dimensions, buffer};
}
template<class...Is, std::enable_if_t<sizeof...(Is) == N>>
T& operator()(size_t i, Is...is)&&{
return std::move(*this)[i](is...);
}
};
template<class T>
struct array_index<T,0> {
size_t const* dimension;
size_t offset;
T* buffer;
T& operator[](size_t i)&&{
return buffer[i+offset];
}
T& operator()(size_t i)&&{
return std::move(*this)[i];
}
};
template<class T, size_t N>
struct n_dim_array {
template<class...Szs, class=std::enable_if_t<sizeof...(Szs)==N>>
explicit n_dim_array( Szs... sizes ):
szs{ { static_cast<size_t>(sizes)... } }
{
size_t sz = 1;
for( size_t s : szs )
sz *= s;
buffer.resize(sz);
}
n_dim_array( n_dim_array const& o ) = default;
n_dim_array& operator=( n_dim_array const& o ) = default;
using top_level_index = array_index<T,N-1>;
top_level_index index(){return {szs.data(),0,buffer.data()};}
auto operator[]( size_t i ) {
return index()[i];
}
using const_top_level_index = array_index<const T,N-1>;
const_top_level_index index()const{return {szs.data(),0,buffer.data()};}
auto operator[]( size_t i ) const {
return index()[i];
}
template<class...Is,class=std::enable_if_t<sizeof...(Is)==N>>
T& operator()(Is...is){
return index()(is...);
}
template<class...Is,class=std::enable_if_t<sizeof...(Is)==N>>
T const& operator()(Is...is) const {
return index()(is...);
}
private:
n_dim_array() = delete;
std::array<size_t,N> szs;
std::vector<T> buffer;
};
live example
Does not support for(:) loop iteration. Writing an iterator isn't that hard: I'd do it in array_index.
I need to dynamically allocate 1-D and 2-D arrays whose sizes are given at run-time.
I managed to "discover" std::vector and I think it fits my purposes, but I would like to ask whether what I've written is correct and/or can be improved.
This is what I'm doing:
#include <vector>
typedef std::vector< std::vector<double> > matrix;
//... various code and other stuff
std::vector<double> *name = new std::vector<double> (size);
matrix *name2 = new matrix(sizeX, std::vector<double>(sizeY));
Dynamically allocating arrays is required when your dimensions are given at runtime, as you've discovered.
However, std::vector is already a wrapper around this process, so dynamically allocating vectors is like a double positive. It's redundant.
Just write (C++98):
#include <vector>
typedef std::vector< std::vector<double> > matrix;
matrix name(sizeX, std::vector<double>(sizeY));
or (C++11 and later):
#include <vector>
using matrix = std::vector<std::vector<double>>;
matrix name(sizeX, std::vector<double>(sizeY));
You're conflating two issues, dynamic allocation and resizable containers. You don't need to worry about dynamic allocation, since your container does that for you already, so just say it like this:
matrix name(sizeX, std::vector<double>(sizeY));
This will make name an object with automatic storage duration, and you can access its members via name[i][j].
What you're doing should basically work, however:
In general, don't dynamically allocate objects
If you want a vector, do this:
std::vector<double> vec(size);
not this:
std::vector<double>* vec = new std::vector<double>(size);
The latter gives you a pointer, which you have to delete. The former gives you a vector which, when it goes out of scope, cleans up after itself. (Internally, of course, it dynamically allocates objects, but the trick is that this is handled by the class itself, and you don't need to worry about it in your user code).
It is correct but could be made more efficient.
You could use the boost multidimensional arrays:
http://www.boost.org/doc/libs/1_47_0/libs/multi_array/doc/user.html
Or, you can implement your own class for it and handle the indexing yourself.
Perhaps something like this (which is not well tested):
#include <vector>
#include <cassert>
template <typename T, typename A = std::allocator<T> >
class Array2d
{
public:
typedef Array2d<T> self;
typedef std::vector<T, A> Storage;
typedef typename Storage::iterator iterator;
typedef typename Storage::const_iterator const_iterator;
Array2d() : major_(0), minor_(0) {}
Array2d(size_t major, size_t minor)
: major_(major)
, minor_(minor)
, storage_(major * minor)
{}
template <typename U>
Array2d(size_t major, size_t minor, U const& init)
: major_(major)
, minor_(minor)
, storage_(major * minor, u)
{
}
iterator begin() { return storage_.begin(); }
const_iterator begin() const { return storage_.begin(); }
iterator end() { return storage_.end(); }
const_iterator end() const { return storage_.end(); }
iterator begin(size_t major) {
assert(major < major_);
return storage_.begin() + (major * minor_);
}
const_iterator begin(size_t major) const {
assert(major < major_);
return storage_.begin() + (major * minor_);
}
iterator end(size_t major) {
assert(major < major_);
return storage_.begin() + ((major + 1) * minor_);
}
const_iterator end(size_t major) const {
assert(major < major_);
return storage_.begin() + ((major + 1) * minor_);
}
void clear() {
storage_.clear();
major_ = 0;
minor_ = 0;
}
void clearResize(size_t major, size_t minor)
{
clear();
storage_.resize(major * minor);
major_ = major;
minor_ = minor;
}
void resize(size_t major, size_t minor)
{
if ((major != major_) && (minor != minor_))
{
Array2d tmp(major, minor);
swap(tmp);
// Get minimum minor axis
size_t const dist = (tmp.minor_ < minor_) ? tmp.minor_ : minor_;
size_t m = 0;
// copy values across
for (; (m < tmp.major_) && (m < major_); ++m) {
std::copy(tmp.begin(m), tmp.begin(m) + dist, begin(m));
}
}
}
void swap(self& other)
{
storage_.swap(other.storage_);
std::swap(major_, other.major_);
std::swap(minor_, other.minor_);
}
size_t minor() const {
return minor_;
}
size_t major() const {
return major_;
}
T* buffer() { return &storage_[0]; }
T const* buffer() const { return &storage_[0]; }
bool empty() const {
return storage_.empty();
}
template <typename ArrRef, typename Ref>
class MajorProxy
{
ArrRef arr_;
size_t major_;
public:
MajorProxy(ArrRef arr, size_t major)
: arr_(arr)
, major_(major)
{}
Ref operator[](size_t index) const {
assert(index < arr_.minor());
return *(arr_.buffer() + (index + (major_ * arr_.minor())));
}
};
MajorProxy<self&, T&>
operator[](size_t major) {
return MajorProxy<self&, T&>(*this, major);
}
MajorProxy<self const&, T const&>
operator[](size_t major) const {
return MajorProxy<self&, T&>(*this, major);
}
private:
size_t major_;
size_t minor_;
Storage storage_;
};
While the points the other answers made were very correct (don't dynamically allocate the vector via new, but rather let the vector do the allocation), if you are thinking terms of vectors and matrices (e.g. linear algebra), you might want to consider using the Eigen matrix library.
You don't allocate containers dynamically. They can automatically manage memory for you if they themselves are not manually managed.
A vector grows when you add new items with push_back (or insert), you can choose its size from the start with arguments to the constructor, and you can resize it later with the resize method.
Creating a vector of vectors with your sizes with the constructor looks like this:
std::vector< std::vector<double> > matrix(size, std::vector<double>(sizeY));
This means: size instances of a std::vector<double>, each containing sizeY doubles (initialized to 0.0).
Sometimes you don't want to fill your stack and your memory requirement is large. Hence you may want to use vector> created dynamically especially while creating a table of a given row and col values.
Here is my take on this in C++11
int main() {
int row, col;
std::cin >> row >> col;
auto *arr = new std::vector<std::vector<int>*>(row);
for (int i=0; i<row; i++) {
auto *x = new std::vector<int>(col, 5);
(*arr)[i] = x;
}
for (int i=0; i<row; i++) {
for(int j=0; j<col; j++) {
std::cout << arr->at(i)->at(j) << " ";
}
std::cout << std::endl;
}
return 0;
}
#include < iostream >
#include < vector >
using namespace std;
int main(){
vector<int>*v = new vector<int>(); // for 1d vector just copy paste it
v->push_back(5);
v->push_back(10);
v->push_back(20);
v->push_back(25);
for(int i=0;i<v->size();i++){
cout<<v->at(i)<<" ";
}
cout<<endl;
delete v;
system("pause");
return 0;
}
If you don't need to resize the array sizes at run time, then you can just use standard arrays (allocated at runtime)!
However, if you do need to resize arrays at runtime, then you can use the following (revised) code:
#include <vector>
typedef std::vector< std::vector<double> > matrix;
//... various code and other stuff
std::vector<double> *name = new std::vector<double> (size);
matrix *name2 = new matrix(sizeX, std::vector<double>(sizeY));
In essence, all I've done is remove a single bracket (().
Consider the task of writing an indexable class which automatically synchronizes its state with some external data-store (e.g. a file). In order to do this the class would need to be made aware of changes to the indexed value which might occur. Unfortunately the usual approach to overloading operator[] does not allow for this, for example...
Type& operator[](int index)
{
assert(index >=0 && index < size);
return state[index];
}
I there any way to distinguish between a value being accessed and a value being modified?
Type a = myIndexable[2]; //Access
myIndexable[3] = a; //Modification
Both of these cases occur after the function has returned. Is there some other approach to overloading operator[] which would perhaps make more sense?
From the operator[] you can only really tell access.
Even if the external entity uses the non cost version this does not mean that a write will take place rather that it could take place.
As such What you need to do is return an object that can detect modification.
The best way to do this is to wrap the object with a class that overrides the operator=. This wrapper can then inform the store when the object has been updated. You would also want to override the operator Type (cast) so that a const version of the object can be retrieved for read accesses.
Then we could do something like this:
class WriteCheck;
class Store
{
public:
Type const& operator[](int index) const
{
return state[index];
}
WriteCheck operator[](int index);
void stateUpdate(int index)
{
// Called when a particular index has been updated.
}
// Stuff
};
class WriteCheck
{
Store& store;
Type& object;
int index;
public: WriteCheck(Store& s, Type& o, int i): store(s), object(o), index(i) {}
// When assignment is done assign
// Then inform the store.
WriteCheck& operator=(Type const& rhs)
{
object = rhs;
store.stateUpdate(index);
}
// Still allow the base object to be read
// From within this wrapper.
operator Type const&()
{
return object;
}
};
WriteCheck Store::operator[](int index)
{
return WriteCheck(*this, state[index], index);
}
An simpler alternative is:
Rather than provide the operator[] you provide a specific set method on the store object and only provide read access through the operator[]
You can have (the non-const) operator[] return a proxy object that keeps a reference or pointer to the container, and in which operator= signals the container of the update.
(The idea of using const vs non-const operator[] is a red herring... you may know that you've just given away non-const access to the object, but you don't know if that access is still being used for a read or a write, when that write completes, or have any mechanism for updating the container thereafter.)
Another elegant (IMHO) solution...
Actually it is based on the fact that the const overload is called only when used on const object.
Lets first create two [] overloads - as it is required, but using different locations:
Type& operator[](int index)
{
assert(index >=0 && index < size);
return stateWrite[index];
}
const Type& operator[](int index) const
{
assert(index >=0 && index < size);
return stateRead[index];
}
Now you should create a shadow reference of your object when you need to "read" it as follows:
const Indexable& myIndexableRead = myIndexable; // create the shadow
Type a = myIndexableRead[2]; //Access
myIndexable[3] = a; //Modification
Creating this shadow declaration does not actually create anything in the memory. It just creates another name for your object with "const" access. It is all resolved at the compilation stage (including usage of const overload) and does not affect anything in runtime - neither memory nor performance.
And the bottom line - it is much more elegant (IMHO) than creating any assignment proxies, etc. I must state that the statement "From the operator[] you can only really tell access" is incorrect. According to the C++ Standard, returning dynamically allocatted object or global variable by reference is ultimate way to allow its direct modification, including [] overload case.
Following code has been tested:
#include <iostream>
using namespace std;
class SafeIntArray {
int* numbers;
int size;
static const int externalValue = 50;
public:
SafeIntArray( unsigned int size = 20 ) {
this->size = size;
numbers = new int[size];
}
~SafeIntArray() {
delete[] numbers;
}
const int& operator[]( const unsigned int i ) const {
if ( i < size )
return numbers[i];
else
return externalValue;
}
int& operator[]( const unsigned int i ) {
if ( i < size )
return numbers[i];
else
return *numbers;
}
unsigned int getSize() { return size; }
};
int main() {
SafeIntArray arr;
const SafeIntArray& arr_0 = arr;
int size = arr.getSize();
for ( int i = 0; i <= size ; i++ )
arr[i] = i;
for ( int i = 0; i <= size ; i++ ) {
cout << arr_0[i] << ' ';
}
cout << endl;
return 0;
}
And the results are:
20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 50
Return a proxy object which will have:
operator=(Type const &) overloaded for writes
operator Type() for reads
in the access example you give you can get a distinction by using a const version:
const Type& operator [] ( int index ) const;
on a sidenote, using size_t as index gets rid of the need for checking if index >= 0
#include "stdafx.h"
#include <iostream>
template<typename T>
class MyVector
{
T* _Elem; // a pointer to the elements
int _Size; // the size
public:
// constructor
MyVector(int _size):_Size(_size), _Elem(new T[_size])
{
// Initialize the elemets
for( int i=0; i< _size; ++i )
_Elem[i] = 0.0;
}
// destructor to cleanup the mess
~MyVector(){ delete []_Elem; }
public:
// the size of MyVector
int Size() const
{
return _Size;
}
// overload subscript operator
T& operator[]( int i )
{
return _Elem[i];
}
};
int _tmain(int argc, _TCHAR* argv[])
{
MyVector<int> vec(10);
vec[0] =10;
vec[1] =20;
vec[2] =30;
vec[3] =40;
vec[4] =50;
std::cout<<"Print vector Element "<<std::endl;
for (int i = 0; i < vec.Size(); i++)
{
std::cout<<"Vec["<<i<<"] = "<<vec[i]<<std::endl;
}
return 0;
}