Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Initialize 2D array in constructor of CPP class

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):

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

>Solution :

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading