1

I was wondering what the best way to initialize a 2D array in a cpp class would be. I do not know its size until the constructor is called, ie,

Header file contains:

private:
    int size;
    bool* visited;
    int edges;
    int** matrix;

Default constructor (right now):

Digraph::Digraph(int n) {
  int rows = (n * (n-1)/2);
  int columns = 2;

  matrix = new int[rows][2];

  visited[size] = { 0 };

  size = n;
  edges = 0;
}

What I want is a 2D array of N rows and 2 columns.

This currently returns error: cannot convert 'int (*)[2]' to 'int**' in assignment when I try to compile.

NOTE: I cannot use Vectors, so please don't suggest them.

1 Answer 1

2

matrix = new int[rows][2]; is not valid syntax. Allocating a 2D sparse array requires multiple new[] calls, eg:

private:
    int size;
    bool* visited;
    int edges;
    int** matrix;
    int rows;
    int columns;

...

Digraph::Digraph(int n) {
  size = n;
  edges = 0;

  rows = (n * (n-1)/2);
  columns = 2;

  matrix = new int*[rows];
  for(int x = 0; x < rows; ++x) {
    matrix[x] = new int[columns];
    for(int y = 0; y < columns; ++y)
      matrix[x][y] = 0;
  }

  visited = new bool[size];
  for(int x = 0; x < size; ++x)
    visited[x] = false;
}

Digraph::~Digraph() {
  for(int x = 0; x < rows; ++x) {
    delete[] matrix[x];
  }
  delete[] matrix;

  delete[] visited;
}

Alternatively, consider allocating the matrix as a 1D array, and then using 2D indexes when accessing its values, eg:

private:
    int size;
    bool* visited;
    int edges;
    int* matrix; // <-- 1 *, not 2 **
    int rows;
    int columns;

    int& matrix_value(int row, int col) { return matrix[(row * rows) + col]; }

...

Digraph::Digraph(int n) {
  size = n;
  edges = 0;

  rows = (n * (n-1)/2);
  columns = 2;

  n = rows * columns;
  matrix = new int[n];
  for(int x = 0; x < n; ++x)
    matrix[n] = 0;

  visited = new bool[size];
  for(int x = 0; x < size; ++x)
    visited[x] = false;
}

Digraph::~Digraph() {
  delete[] matrix;
  delete[] visited;
}

Either way, you will also need to implement (or disable) a copy constructor and copy assignment operator, and preferably a move constructor and move assignment operator, per the Rule of 3/5/0, eg:

Digraph::Digraph(const Digraph &src) {
  size = src.size;
  edges = src.edges;

  rows = src.rows;
  columns = src.columns;

  matrix = new int*[rows];
  for(int x = 0; x < rows; ++x) {
    matrix[x] = new int[columns];
    for (int y = 0; y < columns; ++y)
      matrix[x][y] = src.matrix[x][y];
  }

  /* or:
  n = rows * columns;
  matrix = new int[n];
  for(int x = 0; x < n; ++x)
    matrix[n] = src.matrix[n];
  */

  visited = new bool[size];
  for(int x = 0; x < size; ++x)
    visited[x] = src.visited[x];
}

Digraph::Digraph(Digraph &&src) {
  size = 0;
  edges = 0;
  rows = 0;
  columns = 0;
  matrix = nullptr;
  visited = nullptr;

  src.swap(*this);
}

void Digraph::swap(Digraph &other) {
  std::swap(size, other.size);
  std::swap(edges, other.edges);
  std::swap(rows, other.rows);
  std::swap(columns, other.columns);
  std::swap(matrix, src.matrix);
  std::swap(visited, src.visited);
}

Digraph& Digraph::operator=(Digraph rhs) {
    Digraph temp(std::move(rhs));
    temp.swap(*this);
    return this;
}

That being said, a better design would be to use std::vector instead of new[], and let it handle all of the memory management and copying/moving for you, eg:

#include <vector>

private:
    int size;
    std::vector<bool> visited;
    int edges;
    std::vector<std::vector<int>> matrix;
    // or: std::vector<int> matrix;
    int rows;
    int columns;

...

Digraph::Digraph(int n) {
  size = n;
  edges = 0;

  rows = (n * (n-1)/2);
  columns = 2;

  matrix.resize(rows);
  for(int x = 0; x < rows; ++x)
      matrix[x].resize(columns);

  /* or:
  matrix.resize(rows * columns);
  */

  visited.resize(size);
}

// implicitly-generated copy/move constructors, copy/move assignment operators,
// and destructor will suffice, so no need to implement them manually...

If you can't use std::vector, consider implementing your own vector class with the proper semantics, and then use that instead. You should really strive to follow the Rule of 0 as much as possible, by using classes that implement the Rule of 3/5 for you.

Sign up to request clarification or add additional context in comments.

4 Comments

This worked for me, thanks a ton. And yes, a vector would be a better solution as it would handle all the memory for me. But unfortunately, I am not allowed to use them at this current point. :/
You could always just write your own vector class...
@Flip While this sounds like a waste of time now, you're going to keep getting assignments where you can't use std::vector and having a resizable array class could have saved you tonnes of time. Don't wait until you've had to hack out the same array code three times for three different assignments. Write it once, bundle it into a class, and use the class over and over until you're finally allowed to use std::vector Same will apply to std::string.
@user4581301 while I would love to be able to do that, we are unfortunately limited to what we can upload, with only certain files being allowed to be uploaded and only certain classes allowed to be used. Otherwise, I would do that. Thanks for the tip though!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.