PDA

View Full Version : Problem with debugger and QVector



Computer Hater
4th May 2011, 21:16
Hi,

I seem to have a problem with the gdb debugger in combination with the QVector class.
I have Qt SDK 1.1 RC installed on my laptop with Windows Vista Home Edition.

Here is what happened:
My program crashed, so I debugged it. When it crashes during debugging, I get the attached error message. (I get a different one when I just run it.) The build configuration is set to Debug.
I traced the error back to the destructor of a custom class that has two QVector<int> member variables. Apparently, the error happens when a QVector instance is destroyed as part of the destruction of that class.

Now, when I go through the debugging step by step I get a strange behaviour. I suppose this is related to the crash:

The QVector<int> instances get set normally and their values are accessible through the watcher list. They are actually part of a larger class structure, going up a few levels. Then, when I append the topmost object of this structure (object "newsp" of class Species in the code below) to another QVector, the values of the int vectors are suddenly not accessible anymore. They are shown as 'not accessible' and when I click on them the note changes to 'unavailable synchronous data'.
This is shown both for the original object that I pass to the append routine of QVector and the newly created element of the QVector.

The segment of code in which the error happens is this:



do
{
Species newsp(chem, file, grnames); // create the Species object
if (newsp.valid) spec.append(newsp);
else break;
} while (true);


I'm assuming that QVector::append creates a copy of the object, so it shouldn't matter that the object is created only locally. Even if so, the error appears the very next line after the append command, not just when the loop is looped.
Also, the following code using a pointer causes the exact same problem:



Species* newsp;
do
{
newsp = new Species(chem, file, grnames); // create the Species object
if (newsp->valid) spec.append(*newsp);
else break;
} while (true);


The error appears in the very first iteration of the loop, so no successful instances before that.

I tried to reproduce the error in a trimmed sample program with a QVector<int> object inside another class' object that is then added to a QVector of the topmost class type, but it doesn't appear.
Now I could approach the two versions step by step, but it seemed better to ask first if someone can identify the problem from this description.

Added after 6 minutes:

This is the error I get for a regular program run.
(Edit: the runtime library error. The two posts were merged.)

Also, could it matter that I'm keeping the file open during the loop that I'm reading the Species objects from ?

Zlatomir
4th May 2011, 22:17
Is Species copyable? Do you have pointers in that class (have you wrote copy constructor and assignment operator)? or it's a QObject derived class (QObjects are not copyable)? or there it other "special" thing about that class?

Computer Hater
5th May 2011, 14:48
Well, I did provide a default constructor that doesn't do anything. Could that be a problem ? I did a Google search on that when first trying to add the objects to the vector because I got a compiler error off it (IIRC) and one of the results said a dummy default constructor would be enough.

I have no assignment operator or copy constructor.
There are some pointers in the class.
It is not QObject derived. (Though, just as a side question, if it were, could I copy it anyway by providing the proper custom mechanisms ?)

The header of the class is this:



class Species
{
public:
static const int menudim1 = 2;
static const int menudim2 = 4;
static const int enthalpy_count = 7;
static const int sp_temp_count = 6;

ChemicalData* chem;

bool valid;
QString name;
bool has_formula;
int group;
QString backspec_name;
int backspec;
bool menupos[menudim1][menudim2];

double convfac;
double molweight;
double k_power;
double k_collision;
double enthalpy[enthalpy_count];
double sp_temp[sp_temp_count];

ChemicalSubstance* formula;
bool hydrocarbon;

public:
Species();
Species(ChemicalData* chem, QTextStream &file, const QVector<QString> &groupnames);
~Species();

void readDataFromFile(QTextStream &file, const QVector<QString> &groupnames);
void setBackSpeciesIndex (int index);
void displaySelf();
};


The object of the class "ChemicalSubstance" that is referenced here through a pointer contained the actual QVector<int> variables that displayed the problem ('elements' and 'amounts').
The header of that class is the following:



class ChemicalSubstance
{

enum LetterType {blank, number, upper, lower, open, close};

public:

ChemicalData* chem;
int elcount;
QVector<int> elements;
QVector<int> amounts;
bool valid;

public:
ChemicalSubstance(ChemicalData* chem, QVector<QString> elementlist, QVector<int> amountlist);
ChemicalSubstance(ChemicalData* chem, QString formula);
~ChemicalSubstance();

bool containsElements(QVector<QString> elms);
bool containsElements(QString elms1, QString elms2 = "", QString elms3 = "", QString elms4 = "", QString elms5 = "",
QString elms6 = "", QString elms7 = "", QString elms8 = "", QString elms9 = "", QString elms10 = "");
bool containsOnlyElements(QVector<QString> elms);
bool containsOnlyElements(QString elms1, QString elms2 = "", QString elms3 = "", QString elms4 = "", QString elms5 = "",
QString elms6 = "", QString elms7 = "", QString elms8 = "", QString elm9 = "", QString elms10 = "");
bool containsExactlyTheseElements(QVector<QString> elms);
bool containsExactlyTheseElements(QString elms1, QString elms2 = "", QString elms3 = "", QString elms4 = "", QString elms5 = "",
QString elms6 = "", QString elms7 = "", QString elms8 = "", QString elms9 = "", QString elms10 = "");
bool isHydroCarbon();

};


The pointers in this code to ChemicalData are references to independently existing objects (the same one actually).
The pointer to ChemicalSubstance is in fact to a locally created object connected to the class. It is set to NULL as a default in the Species constructor in case it's not used. Therefore the Species destructor looks like this:



Species::~Species()
{
if (formula) delete formula;
}

Zlatomir
5th May 2011, 15:04
If you have pointer member in your class you will need to write a copy constructor (to copy the memory "indicated" by the pointer and not only the pointer - as the default copy constructor does) and assignment operator too.

If you don't (like you are doing it now) when the QVector copy an element your application has two objects which contains two pointers to only one allocated memory block (which gets deleted when the first destructor is called).

Computer Hater
5th May 2011, 15:50
Of course ! That makes absolute sense. How could I miss that ? You're brilliant ! :D
Another giveaway was that also the non-QVector variables suddenly changed values to some random data. But I didn't notice that because they weren't shown as 'inaccessible' (obviously).

But one thing that doesn't seem to make sense:
When I parse the code with the debugger line by line, the memory is gone the very moment I pass over the append command, not when the loop iteration is done.
But the appended object shouldn't be deleted until the scope of the current loop iteration is left, right ? Or is there a desctructor being called by QVector's append command ?

The original code where the memory loss occurs is this:



do
{
Species newsp(chem, file, grnames);
if (newsp.valid) spec.append(newsp);
else break;
} while (true);


If I modify it to this, I get the same behaviour:



do
{
Species newsp(chem, file, grnames); // create the Species object
if (newsp.valid) spec.append(newsp);
else break;

int i = 0;
i++;
int j = i;
j++;
i = j;
qDebug() << i << j << endl;
} while (true);


I added the garbage code to give the debugger more lines to execute while I look at the memory in the watcher list.
I added the qDebug() output to (hopefully) prevent the compiler from optimizing the code away. (As I would expect it not to remove it if an output of the data is still to come.)

Or does the compiler perhaps delete the object "newsp" right away, seeing that it's not being used anymore in its lifetime ?

Added after 14 minutes:

Update:

I added the following line of code to the loop:



qDebug() << i << newsp.name << endl;


to make sure "newsp" will not be obsolete before the loop ends, but the ChemicalFormula data in it is still deleted before the loop ends.

But now I realize:
At some point I got a peek into the QVector source code (can't seem to find it now) and think I saw that QVector creates a dummy copy somewhere along the line. It's probably this dummy copy that gets deleted and causes the pointed to object to disappear as a consquence.