Home > Net >  assignment and retreval using subscript and equals operator overloads
assignment and retreval using subscript and equals operator overloads

Time:11-16

I've been having a hell of a time trying to solve this. What I am trying to do is use operator overloading so that my objects behave more like a multi dimensional array. I've found solutions to several of the smaller problems involved in making this happen but whenever I try to put it all together there is one issue or another, either lvalue assignment error or invalid initialization from rvalue or just straight up seg fault. I would appreciate any advice TY.

#include <iostream>
#include <vector>
#include <string> 

class Matrix {
  std::string **m;
 public:
  Matrix(int x, int y) {
    m = new std::string*[x];
    for (int i = 0; i < x; i  )
      m[x] = new std::string[y];
  }
  class Proxy {
    std::string *mm;
    int lastIndex = 0;
  public:
    Proxy(std::string *s) : mm(s) {}
      std::string &operator[](int index) {
      lastIndex = index;
      return mm[index];
    }
    std::string &operator=(std::string s) {
      mm[lastIndex] = s;
      return mm[lastIndex];
    }
  };
  Proxy operator[](int index) {
    return Proxy(m[index]);
  }
};

int main()
{
  Matrix *m = new Matrix(5, 5);
  m[2][2] = std::string("It Works");
  std::cout << m[2][2] << std::endl;
  return 0;

CodePudding user response:

In main(), m is a pointer to a Matrix object, so you need to dereference the pointer in order to access the Matrix object so you can invoke your Matrix::operator[] on it, eg:

int main()
{
  Matrix *m = new Matrix(5, 5);
  (*m)[2][2] = "It Works";
  std::cout << (*m)[2][2] << std::endl;
  delete m;
  return 0;
}

Online Demo

Otherwise, the pointer is not really needed in your example to begin with, eg:

int main()
{
  Matrix m(5, 5);
  m[2][2] = "It Works";
  std::cout << m[2][2] << std::endl;
  return 0;
}

Online Demo

Either way, your Proxy does not need to implement operator= at all, eg:

class Proxy {
  std::string *mm;
public:
  Proxy(std::string *s) : mm(s) {}

  std::string& operator[](int index) {
    return mm[index];
  }
};

A statement like m[2][2] = "..."; will not invoke your Proxy::operator=, it will invoke only Proxy::operator[]. A statement like m[2] = "..."; would be needed to invoke Proxy::operator=, which doesn't make sense to do in a multi-dimensional scenario.


Also, your Matrix constructor has a bug - writing to m[x] is going out of bounds of the m[] array, so the array is not actually filled at all, and you are corrupting surrounding memory, and leaking memory. You need to write to m[i] instead:

//m[x] = new std::string[y];
m[i] = new std::string[y];

After fixing that, Matrix is still leaking memory, as it does not implement a destructor to free the std::strings. You must delete[] anything you new[] (same with delete and new).

And then, you should finish off implementing support for the Rule of 3/5/0, by implementing a copy constructor and a copy assignment operator (your example code does not need them, but production code should always have them), eg:

#include <iostream>
#include <string> 
#include <utility>

class Matrix {
    std::string **m;
    int m_x, m_y;
public:
    Matrix(int x = 0, int y = 0) : m_x(x), m_y(y) {
        m = new std::string*[x];
        for (int i = 0; i < x;   i)
            m[i] = new std::string[y];
    }

    Matrix(const Matrix &src) : m_x(src.m_x), m_y(src.m_y) {
        m = new std::string*[m_x];
        for (int i = 0; i < m_x;   i) {
            m[i] = new std::string[m_y];
            for (int j = 0; j < m_y;   j) {
                m[i][j] = src.m[i][j];
            }
        }
    }

    ~Matrix() {
        for (int i = 0; i < m_x;   i)
            delete[] m[i];
        delete[] m;
    }

    Matrix& operator=(const Matrix &rhs) {
        if (&rhs != this) {
            Matrix temp(rhs);
            std::swap(m, temp.m);
            std::swap(m_x, temp.m_x);
            std::swap(m_y, temp.m_y);
        }
        return *this;
    }
    
    class Proxy {
        std::string *mm;
    public:
        Proxy(std::string *s) : mm(s) {}

        std::string& operator[](int index) {
            return mm[index];
        }
    };

    Proxy operator[](int index) {
        return Proxy(m[index]);
    }
};

int main()
{
    Matrix m(5, 5);
 
    m[2][2] = "It Works";
    std::cout << m[2][2] << std::endl;
 
    Matrix m2(m);
    std::cout << m2[2][2] << std::endl;
 
    Matrix m3;
    m3 = m2;
    std::cout << m3[2][2] << std::endl;

    return 0;
}

Online Demo

However, rather than using new[] manually, consider using std::vector instead (which you are already aware of, since you have #include <vector> in your code). This way, the Rule of 3/5/0 can be handled entirely by the compiler for you. std::vector and std::string are both fully compliant with the Rule, and so any compiler-generated destructor, copy constructor, and copy-assignment operator in Matrix will suffice, eg:

#include <iostream>
#include <vector>
#include <string> 

class Matrix {
    std::vector<std::vector<std::string>> m;
public:
    Matrix(int x = 0, int y = 0) {
        m.resize(x);
        for (int i = 0; i < x;   i)
            m[i].resize(y);
    }

    class Proxy {
        std::vector<std::string> &mm;
    public:
        Proxy(std::vector<std::string> &s) : mm(s) {}

        std::string& operator[](int index) {
            return mm[index];
        }
    };

    Proxy operator[](int index) {
        return Proxy(m[index]);
    }
};

Online Demo

  • Related