operator overloading [][] 2d array c++
Asked Answered
S

5

12

I have a 2D array and I want to define a function that returns the value of the index that the user gives me using operator overloading. In other words:

void MyMatrix::ReturnValue()
{
    int row = 0, col = 0;
    cout << "Return Value From the last Matrix" << endl;
    cout << "----------------------------------" << endl;
    cout << "Please Enter the index: [" << row << "][" << col << "] =" << ((*this).matrix)[row][col] << endl;
}

The operation ((*this).matrix)[row][col] should return an int. I have no idea how to build the operator [][].
Alternatively, I could concatenate a couple of calls to the operator [], but I didn't succeed in it, because the first call to that operaror will return int* and the second one will return int, and it compel to build another operator, and I dont want to do that.

The data matrix is defined like

int** matrix; matrix = new int*[row];
if (matrix == NULL)
{
    cout << "Allocation memory - Failed";
}
for (int i = 0; i < row; i++)//Allocation memory
{
    matrix[i] = new int[col];
    if (matrix[i] == NULL)
    {
        cout << "Allocation memory - Failed";
        return;
    }
}

What can I do? Thank you,

Supralapsarian answered 25/12, 2015 at 8:50 Comment(4)
Simply, such an operator does not exist, so you cannot overload it.Labdanum
Ok, It means that I have to build two function for the operator [], because if I do it like: (((*this).matrix)[row])[col] so the first operator will return int* and the second int.Supralapsarian
@lilach Show how the data member matrix is defined.Rebbeccarebe
int** matrix; matrix = new int*[row]; if (matrix == NULL) { cout << "Allocation memory - Failed"; } for (int i = 0; i < row; i++)//Allocation memory { matrix[i] = new int[col]; if (matrix[i] == NULL) { cout << "Allocation memory - Failed"; return; } }Supralapsarian
L
14

Simply, such an operator does not exist, so you can not overload it.

A possible solution is to define two classes: the Matrix and the Row.
You can define the operator[] of a Matrix so that it returns a Row, then define the same operator for the Row so that it returns an actual value (int or whatever you want, your Matrix could be also a template).
This way, the statement myMatrix[row][col] will be legal and meaningful.

The same can be done in order to assign a new Row to a Matrix or to change a value in a Row.

* EDIT *

As suggested in the comments, also you should take in consideration to use operator() instead of operator[] for such a case.
This way, there wouldn't be anymore the need for a Row class too.

Labdanum answered 25/12, 2015 at 8:59 Comment(5)
I would also mention that overloading operator () is an easier route that overloading [] to return some custom class with its own operator []. See the C++ FAQs on this respectIrate
Absolutely, it makes sense, but I've understood that the target of the OP is to use a statement like myMatrix[row][col] and that's why I went on with a solution that uses operator[]. Anyway, good hint, I'm going to modify the response.Labdanum
How the LINKER distinguish between the function of ROW and Matrix. both get int. they have the same signing. At the first time the operator get the index row and return pointer to that row(int), than it get index col (int)Supralapsarian
@lilach right, but they belong to different classes. :-)Labdanum
const int MyMatrix::operator[](const int index)const const int* Row::operator[](const int index)constSupralapsarian
R
9

You can define your own operator [] for the class. A straightforward approach can look the following way

#include <iostream>
#include <iomanip>

struct A
{
    enum { Rows = 3, Cols = 4 };
    int matrix[Rows][Cols];
    int ( & operator []( size_t i ) )[Cols]
    {
        return matrix[i];
    }
};

int main()
{
    A a;

    for ( size_t i = 0; i < a.Rows; i++ )
    {
        for ( size_t j = 0; j < a.Cols; j++ ) a[i][j] = a.Cols * i + j;
    }


    for ( size_t i = 0; i < a.Rows; i++ )
    {
        for ( size_t j = 0; j < a.Cols; j++ ) std::cout << std::setw( 2 ) << a[i][j] << ' ';
        std::cout << std::endl;
    }
}

The program output is

 0  1  2  3 
 4  5  6  7 
 8  9 10 11 
Rebbeccarebe answered 25/12, 2015 at 9:50 Comment(8)
Upvoted, even though his colleagues will hurt him for sure once they see this operator. :-) ... I'm still trying to figure out how and why it works!! Thank you, a Christmas gift to my thirst for knowledge.Labdanum
It works because it returns a reference to the selected row of the array, which can then be further indexed with [] - that type is int & [Cols], which is the true result type of matrix[i] before it decays into a pointer. However, the syntax for the function returning such a reference is ugly and confusing, in the same league of member function pointers, so most people do not bother with it and just return a pointer to the first element of the row in question.Irate
Althoug I appreciate a good WAT here and there, might I suggest the use of a trailing return typeWendling
I am not understand, matrix[i] not return int, it is pointer for row i.Supralapsarian
@lilach matrix[i] returns reference to a one-dimensional array. So You may apply operator [] a second time that to get an integer. That is this record a[i][j] will be valid.Rebbeccarebe
may you explain the syntax of: int ( & operator []( size_t i ) )[Cols] { return matrix[i]; } I am trying to implement the function (c++), and I am not understand :1. what is the use of [Cols]? 2. It make use with another function (operator[]`)?Supralapsarian
@lilach To make the definition simpler you can introduce either a using alias or a type def for the return type. For example struct A { enum { Rows = 3, Cols = 4 }; using TRow = int ( & )[Cols]; int matrix[Rows][Cols]; TRow operator []( size_t i ) { return matrix[i]; } };Rebbeccarebe
@lilach Or the following way. This is even more clear struct A { enum { Rows = 3, Cols = 4 }; using TRow = int [Cols]; int matrix[Rows][Cols]; TRow & operator []( size_t i ) { return matrix[i]; } };Rebbeccarebe
F
3

I have no idea how to build the operator [][].

Sometimes it is fine to use a different operator, namely ():

int& Matrix::operator () (int x, int y)
{
    return matrix[x][y];
}

const int& Matrix::operator () (int x, int y) const
{
    return matrix[x][y];
}

int diagonal (const Matrix& m, int x)
{
    return m (x, x); // Usage.
}

Advantage:

  • No need to use "intermediate" class like Row or Column.

  • Better control than with Row& Matrix operator (int); where someone could use the Row reference to drop in a row of, say, illegal length. If Matrix should represent a rectangular thing (image, matrix in Algebra) that's a potential source of error.

  • Might be less tedious in higher dimensions, because operator[] needs classes for all lower dimensions.

Disadvantage:

  • Uncommon, different syntax.

  • No more easy replacement of complete rows / columns, if that's desired. However, replacing columns is not easy, anyway, provided you used rows to model (and vice versa).

In either case, there are pros and cons if the number of dimensions are not known at runtime.

Firn answered 9/3, 2020 at 13:54 Comment(0)
M
1

I was looking for self-tested array replacement... Improved version returns reference or NULL reference and checks boundaries inside.

#include <iostream>
#include <iomanip>

template<typename T, int cols>
class Arr1
{
public:
    Arr1(T (&place)[cols]) : me(place) {};
    const size_t &Cols = cols;
    T &operator [](size_t i)
    {
        if (i < cols && this != NULL) return me[i];
        else {
            printf("Out of bounds !\n");
            T *crash = NULL;
            return *crash;
        }
    }
private:
    T (&me)[cols];
};
template<typename T, int rows, int cols>
class Arr2
{
public:
    const size_t &Rows = rows;
    const size_t &Cols = cols;
    Arr2() {
        ret = NULL;
        for (size_t i = 0; i < rows; i++) // demo - fill member array
        {
            for (size_t j = 0; j < cols; j++) matrix[i][j] = cols * i + j;
        }
    }
    ~Arr2() {
        if (ret) delete ret;
    }
    Arr1<T, cols>(&operator [](size_t i))
    {
        if (ret != NULL) delete ret;
        if (i < rows) {
            ret = new Arr1<T, cols>(matrix[i]);
            return *ret;
        }
        else {
            ret = NULL;
            printf("Out of bounds !\n");
            return *ret;
        }
    }
    //T(&MemberCheck)[rows][cols] = matrix;
private:
    T matrix[rows][cols];
    Arr1<T, cols> *ret;
};
template<typename T,int rows, int cols>
class Arr
{
public:
    const size_t &Rows = rows;
    const size_t &Cols = cols;
    T(&operator [](size_t i))[cols]
    {
        if (i < rows) return matrix[i];
        else {
            printf("Out of bounds !\n");
            T(*crash)[cols] = NULL;
            return *crash;
        }
    }
    T (&MemberCheck)[rows][cols] = matrix;
private:
    T matrix[rows][cols];
};

void main2()
{
    std::cout << "Single object version:" << endl;
    Arr<int, 3, 4> a;

    for (size_t i = 0; i <= a.Rows; i++)
    {
        int *x = &a[i][0];
        if (!x) printf("Fill loop - %i out of bounds...\n", i);
        else for (size_t j = 0; j < a.Cols; j++) a[i][j] = a.Cols * i + j;
    }

    for (size_t i = 0; i < a.Rows; i++)
    {
        for (size_t j = 0; j <= a.Cols; j++) {
            std::cout << std::setw(2) << a[i][j] << ' ';
            if (a.MemberCheck[i][j] != a[i][j])
                printf("Internal error !");
        }
        std::cout << std::endl;
    }

    std::cout << endl << "Double object version:" << endl;

    Arr2<int, 3, 4> a2;
    for (size_t i = 0; i < a2.Rows; i++)
    {
        for (size_t j = 0; j <= a2.Cols; j++) {
            int &x = a2[i][j];
            if (&x)
            {
                x++;
                std::cout << std::setw(2) << a2[i][j] << ' ';
                //if (&a2.MemberCheck[i][j] != &a2[i][j])
                //  printf("Internal error !");
            }
        }
    }
}

Output

Single object version:
Out of bounds !
Fill loop - 3 out of bounds...
 0  1  2  3  4
 4  5  6  7  8
 8  9 10 11 -858993460

Double object version:
 1  2  3  4 Out of bounds !
 5  6  7  8 Out of bounds !
 9 10 11 12 Out of bounds !
Maharajah answered 11/5, 2021 at 17:56 Comment(0)
I
0

it works fine in the program below

#include<iostream>
using namespace std;
class A{
    public:
    int r,c;
    int** val;
    A()
    {
        r=0;c=0;val=NULL;
    }
    A(int row,int col)
    {
        r=row;c=col;
        int count=0;
        val=new int*[row];
        for(int i=0;i<r;i++){
            val[i]=new int[col];
            for(int j=0;j<c;j++){
                count++;
                val[i][j]=count;
            }
        }
    }
    int* &operator[](int index){
        return val[index];
    }
};
int main(void){
    A a(3,3);
    cout<<a[1][2];
    return 0;
}

here, a[1][2] first computes a[1]-->which returns 2nd row as (int*) type then it's read as (int*)[2] which returns 3rd element of that row.In short,

a[1][2]------>(a[1])[2]------>(val[1])[2]------>val[1][2].

Isothere answered 18/3, 2022 at 7:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.