-1

My task is to generate a square matrix of zeros in a function and return it. There are plenty ways to do this, but I decided not to go with returning the matrix by value for efficiency. I went for a pointer approach like in this answer, but since it requires manual cleaning memory (and also as far as I know it's better to use smart pointers), I decided to turn it into std::unique_ptr, but I can't get it to work. This is my code:

#include <iostream>
#include <memory>

std::unique_ptr<std::unique_ptr<int>[] > GenerateMatrix(const int &n) {
    std::unique_ptr<std::unique_ptr<int>[] > matrix(new std::unique_ptr<int>[n]);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            matrix[i].get()[j] = 0;
        }
    }

    return matrix;
}

int main() {
    int n = 4;
    auto matrix = GenerateMatrix(n);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            std::cout<<matrix[j].get()[i]<<" ";
        }
        std::cout<<std::endl;
    }

    return 0;
}

What do I do wrong here? Is this approach even correct?

Elgirhath
  • 409
  • 1
  • 7
  • 15
  • 1
    `std::unique_ptr[]>` is not a 2D matrix. You want `std::unique_ptr[]>`. – HolyBlackCat Mar 27 '19 at 13:51
  • 2
    `std::vector>` possibly wrapped in class. (and if wrapped, flat the vector and do index computation manually for cache friendly) – Jarod42 Mar 27 '19 at 13:51
  • And you miss the initialization of inner (smart) pointers. – Jarod42 Mar 27 '19 at 13:53
  • 2
    You don't benefit from using `std::unique_ptr` in this case. – vahancho Mar 27 '19 at 13:53
  • If you do this you might a well do `std::vector>` which would be better all round, though still not the most efficient solution. – Galik Mar 27 '19 at 13:53
  • 1
    "There are plenty ways to do this, but I decided not to go with returning the matrix by value for efficiency." This assumption is wrong - please understand *return value optimization* and *guarenteed copy elision*. **Morale here:** premature optimization is the root of all evil – darune Mar 27 '19 at 13:55
  • 1
    This may be a useful approach to consider: https://stackoverflow.com/questions/53038457/what-is-the-best-modern-c-approach-to-construct-and-manipulate-a-2d-array/53038618#53038618 – Galik Mar 27 '19 at 13:55
  • @vahancho could you please explain why? – Elgirhath Mar 27 '19 at 13:56
  • 2
    Both copy-elision and move-semantics makes it better to use a plain `std::vector` here and return it by value. – super Mar 27 '19 at 14:02
  • You are not allocating enough memory for a square matrix. If you want a 4x4 matrix, you must allocate space for 16 integers = n*n. – Gardener Mar 27 '19 at 14:13
  • The call `matrix[i].get()[j]` does not make sense. get() returns the managed object. However, by incrementing the pointer first, `matrix[i]` before calling .get(), and then adding j to it, you are venturing into undefinfed beahvior. The correct call is 'matrix.get()[n*i + j]`. The managed object is a 1d array and should be treated as such. – Gardener Mar 27 '19 at 14:16

4 Answers4

1

Why not just make your life easier by

vector<vector<int>> generate (int m, int n)
{
    return vector<vector<int>>(m ,vector<int>(n));
}

int main()
{
    int m = 3, n = 4;
    auto matrix = generate(m, n);  // a 3-by-4 matrix of zeros
    return 0;
}

aafulei
  • 2,085
  • 12
  • 27
  • The issue for perfomance with this solution is that it doesn't store the data contigously in memory. – darune Mar 27 '19 at 14:00
1

Just rely on guarenteed copy elision or return value optimization:

std::vector<int> GenerateMatrix(const int &n) {
    return std::vector<int>(n*n, 0);//, 0 can be omitted (as elements will then be zero-initialized)
}
darune
  • 10,480
  • 2
  • 24
  • 62
0

You might create and initialize a matrix at compile time. For example:

template<int RowCount, int ColumnCount, int DefaultValue = 0>
struct Matrix
{
  static_assert(RowCount >= 0 && ColumnCount >=0,
                "The number of rows and columns should be positive");
  struct Row
  {
    int column[ColumnCount] = { DefaultValue };
  };
  Row row[RowCount];
};

And use it like:

Matrix<2, 2, 33> matrix;
auto val = matrix.row[0].column[0]; // val == 33
matrix.row[0].column[0] = 55;
val = matrix.row[0].column[0]; // val == 55

Beware the matrix dimensions, when refer to its elements by row and column.

vahancho
  • 20,808
  • 3
  • 47
  • 55
-2

You are not allocating enough memory for your matrix. Change this line:

std::unique_ptr<std::unique_ptr<int>[] > matrix(new std::unique_ptr<int>[n*n]);

Also, I would just use i*n + j for your accesses since you are really dealing with a 1D array:

#include <iostream>
#include <memory>

std::unique_ptr<std::unique_ptr<int>[] > GenerateMatrix(const int &n) {
  std::unique_ptr<std::unique_ptr<int>[] > matrix(new std::unique_ptr<int>[n*n]);
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      matrix.get()[i*n+j] = 0;
    }
  }

  return matrix;
}

int main() {
  int n = 4;
  auto matrix = GenerateMatrix(n);
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      std::cout<<matrix.get()[i*n+j]<<" ";
    }
    std::cout<<std::endl;
  }

  return 0;
}
Gardener
  • 2,591
  • 1
  • 13
  • 22