PDA

View Full Version : Defining a two dimensional QVector class (Q2DDoubleVector)



ufechner
25th September 2011, 20:10
Hello,

I am trying to build a class, that can hold a resizable two
dimensional array of double values.

I need this, to hold a matrix of 1e6 rows and 5 or more colums of real-time data.
When the data aquisition is running I want to avoid to allocate memory, because that can be slow.

But I want to be able to change the exact number of rows and columns in a configuration file (and
in a menu option, when the aquisition is not running), and therefore I need a dynamic array.

The following code crashes:



class Q2DDoubleVector : public QVector< QVector<double> > // Overloading Class QVector to allow for 2 dimensional vectors.
{
public:
// constructor
Q2DDoubleVector() : QVector< QVector<double> >(){}
// methods
// matrix resize
void mresize(int rows, int columns);
// destructor
virtual ~Q2DDoubleVector() {}
};

void Q2DDoubleVector::mresize(int rows, int columns)
{
this->resize(rows);
for(int r=0; r<rows; r++) {
this[r].resize(columns);
}
}

Q2DDoubleVector matrix;
matrix.mresize(ROWS, COLS);

If I do it in a very similar way, it doesn't crash:


QVector<QVector <double> > matrix;

matrix.resize(ROWS);
for(int r=0; r < ROWS; r++) {
matrix[r].resize(COLS);
}

Why does the first approach not work?

I don't want to implement the resize loop for each 2D matrix, that I use again.

You can download both projects (the example that works (ArrayTest) and
the example that doesn't work (ArrayTest2) from the following URL:
http://db.tt/IUWpBCnY

Any hints welcome.

Actually, I was using Delphi in the past, and with Delphi this was very easy:


var Matrix: array of array of Double;
begin
SetLength(Matrix, 10, 20)
end;


Best regards:

Uwe Fechner

I am using Ubuntu 11.04 32 bit and QT 4.7.2 .

stampede
25th September 2011, 22:37
If I do it in a very similar way, it doesn't crash:
There is one "small" difference:


(1) this[r].resize(columns);
vs.
(2) matrix[r].resize(COLS);

Line (1) does not call "operator []" on this pointer like (2). Its equivalent to

*(this+r).resize(columns);
Can you see the problem ? Its like you want to access an array of objects using index r, where this should point to the first element. Obviously this array does not exists, so your program crashes.
You can fix this by changing (1) to:


(*this)[r].resize(columns);

IMHO you should not subclass a QVector, it does not have a virtual destructor. Better use composition instead.

----
edit:
I've made a simple example to show this pointer arithmetic "magic":


#include <QDebug>

class Test{
public:
Test(){

}
void check(){
qDebug() << "this is" << x << ", next is:" << this[1].x;
}
int x;
};

int main(){
Test x[5];
for( int i=0 ; i<5 ; ++i ){
x[i].x = i;
}
for( int i=0 ; i<4 ; ++i ){
x[i].check();
}
}
// outputs:
// this is 0 , next is: 1
// this is 1 , next is: 2
// this is 2 , next is: 3
// this is 3 , next is: 4

We create an array of 5 objects, assigned unique, ordered values. In method "check" we can access next element in that array, using pointer arithmetic ( this[1] = *(this+1) = next object in array ).
Don't use it in "real" code, its just for fun ;)

ufechner
25th September 2011, 23:14
Hello,
I tried your suggested change:

(*this)[r].resize(columns);

but this didn't help:

The code still crashes.

Regards:

Uwe

stampede
25th September 2011, 23:22
So use debugger to check where exactly it crashes, I've just tested your class with following code:


class Q2DDoubleVector : public QVector< QVector<double> > // Overloading Class QVector to allow for 2 dimensional vectors.
{
public:
// constructor
Q2DDoubleVector() : QVector< QVector<double> >(){}
// methods
// matrix resize
void mresize(int rows, int columns);
// destructor
virtual ~Q2DDoubleVector() {}
};

void Q2DDoubleVector::mresize(int rows, int columns)
{
this->resize(rows);
for(int r=0; r<rows; r++) {
(*this)[r].resize(columns);
}
}
// in main() :
Q2DDoubleVector m;
int row = 5, col = 6;
m.mresize(row,col);
for( int i=0 ; i<row ; ++i )
for( int j=0 ; j<col ; ++j ){
m[i][j] = (i+1)*(j+1);
qDebug() << m[i][j];
}

This code executed without a crash. Make sure you rebuild the project too.

ufechner
26th September 2011, 07:26
Hello,
I tried again, and now it works fine.


class Q2DDoubleVector : public QVector< QVector<double> > // Overloading Class QVector to allow for 2 dimensional vectors.
{
public:
// constructor
Q2DDoubleVector() : QVector< QVector<double> >(){}
// methods
// matrix resize
void mresize(int rows, int columns);
// destructor
virtual ~Q2DDoubleVector() {}
};

void Q2DDoubleVector::mresize(int rows, int columns)
{
this->resize(rows);
for(int r=0; r<rows; r++) {
(*this)[r].resize(columns);
}
}

Q2DDoubleVector matrix;
matrix.mresize(ROWS, COLS);


Thank's a lot for your help!

Uwe Fechner

stampede
26th September 2011, 09:12
Great, no problem.
But I think you should really consider composition in your code:


class Q2DDoubleVector{
public:
Q2DDoubleVector(){}
void mresize(int rows, int columns);
virtual ~Q2DDoubleVector() {}
protected:
QVector< QVector<double> > _data;
};

void Q2DDoubleVector::mresize(int rows, int columns)
{
_data.resize(rows);
for(int r=0; r<rows; r++) {
_data[r].resize(columns);
}
}

I think this code is much simpler.