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.
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
So I got a function which creates me 2D array and fill it with test data.
Now I need to assign the pointer to an array
//Fill matrix with test data
int *testArrData(int m, int n){
int arr[n][m];
int* ptr;
ptr = &arr[0][0];
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
*((ptr+i*n)+j) = rand()%10;
}
}
return (int *) arr;
}
int arr[m][n];
//Algorithm - transpose
for (int i = 0; i < m; i++){
for (int j = 0; j < n; j++){
arrT[j][i] = arr[i][j];
}
}
Is there any way of doing this?
There are at least four problems with the function.
//Fill matrix with test data
int *testArrData(int m, int n){
int arr[n][m];
int* ptr;
ptr = &arr[0][0];
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
*((ptr+i*n)+j) = rand()%10;
}
}
return (int *) arr;
}
First of all you declared a variable length array
int arr[n][m];
Variable length arrays are not a standard C++ feature.
The second problem is that these for loops
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
*((ptr+i*n)+j) = rand()%10;
}
}
are incorrect. It seems you mean
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
*((ptr+i*m)+j) = rand()%10;
}
}
You are returning a pointer to a local array with automatic storage duration that will not be alive after exiting the function. So the returned pointer will be invalid.
And arrays do not have the assignment operator.
Instead use the vector std::vector<std::vector<int>>. For example
std::vector<std::vector<int>> testArrData(int m, int n){
std::vector<std::vector<int>> v( n, std::vector<int>( m ) );
for ( auto &row : v )
{
for ( auto &item : row )
{
item = rand() % 10;
}
}
return v;
}
This is how I would accomplish this. I agree with int ** because it is easy to understand if you dont know how to use vectors. Also, the rand() can cause trouble if you are using the result to index an array. Make sure to use abs(rand() % number) if you don't want negative numbers.
I've updated the answer due to some vital missing code.
// This method creates the overhead / an array of pointers for each matrix
typedef int* matrix_cells;
int **create_row_col_matrix(int num_rows, int num_cols, bool init_rnd)
{
num_rows = min(max(num_rows, 1), 1000); // ensure num_rows = 1 - 1000
num_cols = min(max(num_cols, 1), 1000); // ensure num_cols = 1 - 1000
int *matrix_total = new int[num_rows*num_cols];
// overhead: create an array that points to each row
int **martix_row_col = new matrix_cells[num_rows];
// initialize the row pointers
for (int a = 0; a < num_rows; ++a)
{
// initialize the array of row pointers
matrix_row_col[a] = &matrix_total[num_cols*a];
}
// assign the test data
if (init_rnd)
{
for (int run_y = 0; run_y < num_rows; ++run_y)
{
for (int run_x = 0; run_x < num_cols; ++run_x)
{
matrix_row_col[run_y][run_x] = abs(rand() % 10);
}
}
}
return matrix_row_col;
}
int src_x = 7, dst_x = 11;
int src_y = 11, dst_y = 7;
int **arr_src = create_row_col_matrix(src_y, src_x, true);
int **arr_dst = create_row_col_matrix(dst_y, dst_x, false);
for (int a = 0; a < dst_y; ++a)
{
for (int b = 0; b < dst_x; ++b)
{
arr_dst[a][b] = arr_src[b][a];
}
}
delete matrix_src[0]; // int *matrix_total = new int[src_y*src_x]
delete matrix_src; // int **matrix_row_col = new matrix_cell[src_y]
delete matrix_dst[0]; // int *matrix_total = new int[dst_y*dst_x]
delete matrix_dst; // int **matrix_row_col = new matrix_cell[dst_y]
// the overhead is matrix_src and matrix_dst which are arrays of row pointers
// the row pointers makes it convenient to address the cells as [rown][coln]
class ClassName {
A* grid[][];
}
I'm getting that "declaration of ‘grid’ as multidimensional array must have bounds for all dimensions except the first" error. The issue is I won't know the dimensions until I run the program, where the size is one of the arguments.
The instructions state that each element of the grid should be an A* -- i.e., a pointer to an object of type A.
How can I do this?
You could opt to define grid this way: A*** grid;. Of course, you'd have to dynamically allocate memory with this setup:
class ClassName {
A*** grid;
int nCols, nRows;
public:
ClassName(int cols, int rows) {
nCols = cols;
nRows = rows;
grid = new A**[nCols];
for (int i = 0; i < nCols; i++) {
grid[i] = new A*[nRows];
for (int j = 0; j < nRows; j++) {
grid[i][j] = nullptr;
}
}
}
~ClassName() {
for (int i = 0; i < nCols; i++) {
delete[] grid[i];
}
delete[] grid;
}
};
here is my question:
I have two classes Vector and Matrix, and I have define two functions, one to calculate the multiplication of a vector and a matrix and the other is to assign the value to a new vector.
Here is the code:
file: Matrix.cpp
Vector Matrix::operator*(const Vector& v)const {
assert(v.length == numRows);
Vector temp(v.length);
for (int j = 0; j < numCols; j++)
for (int k = 0; k < v.length; k++)
temp.contents[j] += v.contents[k] * contents[k][j];
return temp;
};
file: Vector.cpp
Vector& Vector::operator=(Vector& v){
assert(v.length == length);
if (&v != this) {
for (int i = 0; i < length; i++)
setComponent(i, v.contents[i]);
}
return *this;
};
Suppose I have well defined a 4*4 matrix m1 and a 1*4 vector v1
Here is part of my code in main() function,
file: main.app
Vector v2(4);
v2 = m1 * v1;
It can compile but will encounter a problem.
Can anyone give me a hint on how to deal with this? Is it because that I am trying to bind a reference with a return value of a function? Thanks so much!
In you code you defined the assignment operator like this Vector& Vector::operator=(Vector &v). But it should be like Vector& Vector::operator=(Vector const & v). The reason is that Vector &v refers to an lvalue reference. But m1 * v1 returns a rvalue.
Writing an address of 0x00....04 is an offset of 4 bytes from a null ptr. It means that you are trying to write through an uninitialized pointer. If you use a debugger, you can find the exact code trying to do this.
Be careful that you are not having name clash with std::vector.
Presume you have constructor, copy constructor (given below, also needed assignment operator) that allocates properly and initializes all elements to zero
Vector::Vector(int sz) {
contents = new int[length = sz]; // allocation
for (int i = 0; i < sz; i++) {
contents[i] = 0;
}
}
Vector::Vector(const Vector& v) {
contents = new int[length = v.length]; // allocation
for (int i = 0; i < length; i++) {
contents[i] = v.contents[i];
}
}
Matrix::Matrix(int rows, int cols) {
contents = new int *[numRows = rows]; // allocation
for (int i = 0; i < rows; i++) {
contents[i] = new int[numCols = cols]; // allocation
for (int j = 0; j < cols; j++) {
contents[i][j] = 0;
}
}
}
Matrix::Matrix(const Matrix& m) {
contents = new int *[numRows = m.numRows]; // allocation
for (int i = 0; i < numRows; i++) {
contents[i] = new int[numCols = m.numCols]; // allocation
for (int j = 0; j < numCols; j++) {
contents[i][j] = 0;
}
}
}
I have a matrix of 1*5
I defined:
int **mat = new int*[5]; // define the matrix
int* ptr = *mat;
ptr++ gives me the pointer of the next row of the matrix.
I want to get the pointer of the second element of the first row (the location of [0][1]).
how can I do it?
C and C++ have no multi-dimensional arrays. So to do this, you'll have to do extra allocations:
First, allocate your memory:
int rows = 1;
int columns = 5;
int** mat = new int*[columns];
for(int i = 0; i < columns; ++i)
{
mat[i] = new int[rows];
}
Index your matrix:
int x = 1; // Column number, x-movement
int y = 0; // Row number, y-movement
//mat[x][y] = ???;
Don't forget to free memory that you allocated:
for(int i = 0; i < columns; ++i)
{
delete [] mat[i];
}
delete [] mat;
Or, use std::vector. It's much easier to use and understand:
#include <vector>
int rows = 1;
int columns = 5;
std::vector<std::vector<int > > mat = std::vector<std::vector<int > >();
mat.resize(columns);
for(int i = 0; i < 5; ++i)
{
mat[i].resize(rows);
}
//mat[1][0] = 100;
IN MY CODE IT WORKED LIKE THIS(the previous code has a problem for #columns>3 and #rows>3 especially for double precision vectors
#include vector
int rows=1;
int columns=5;
std::vector<std::vector<int > > mat = std::vector<std::vector<int > >();
mat.resize(rows);
for(int i = 0; i <rows; ++i)
{
mat[i].resize(columns)
}
I've absolutely no idea why my delete codes inside the destructor won't be able to functionally well. I hope u guys can help me for this.
Thank you so much!
class Array2D
{
public:
Array2D();
Array2D(int, int);
~Array2D();
private:
int row;
int col;
int **p;
};
Array2D::Array2D()
{
// Default Constructor
}
Array2D::Array2D(int rows, int cols)
{
this -> row = rows;
this -> col = cols;
p = new int* [row];
for(int i=0; i< row; i++)
p[i] = new int[col];
// Fill the 2D array
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
{
p[i][j] = rand () % 100;
}
}
Array2D::~Array2D()
{
// I'm using this way to delete my 2D array.
// however, it won't work!
for (int i = 0; i < row; i++)
{
delete[]p[i];
}
delete[]p;
}
You are not initializing anything in your default constructor. That means that the destructor will go mad on a default constructed object. You are also not disabling the copy constructor, which is not functioning with your class, because if you have copied an object, it will try to delete the same table twice. Change it as follows, for example
class Array2D
{
public:
Array2D();
Array2D(int, int);
~Array2D();
private:
int row;
int col;
int **p;
void initialize(int rows, int cols);
// disable copy functions (make private so they cannot
// be used from outside).
Array2D(Array2D const&);
Array2D &operator=(Array2D const&);
};
Array2D::Array2D()
{
initialize(0, 0);
}
Array2D::Array2D(int rows, int cols)
{
initialize(rows, cols);
}
void Array2D::initialize(int rows, int cols) {
this -> row = rows;
this -> col = cols;
p = new int* [row];
for(int i=0; i< row; i++)
p[i] = new int[col];
// Fill the 2D array
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
{
p[i][j] = rand () % 100;
}
}