Home > OS >  Initialize 2D array in constructor of CPP class
Initialize 2D array in constructor of CPP class

Time:12-02

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.

CodePudding user response:

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.

  • Related