PDA

View Full Version : Why does this crash (compilable code included)?



Krzysztow
6th December 2011, 18:33
Hello All,

probably this is a simple question, however (maybe due to being tired) I cannot see any solution. The code below is extracted and simplified part of my application (these are some factory methods, basing on xml input). However now it magically(?) crashes on something else, than my original application. But still, maybe the reason is the same.

I put both parts in main.cpp file. Hovewer originally have separated declarations and definitions.

Two classes used are as follows:


#include <QBitArray>
#include <QDebug>

//! Base class, with one pure virtual method doActionThatCrashes()
class PropertyConverter {
public:
virtual ~PropertyConverter(){}
virtual void doActionThatCrashes() = 0;
};

//! Inherits PropertyConverter and implements funciton that really crashes.
class PropertyBitmaskConverter:
public PropertyConverter
{
public:
PropertyBitmaskConverter(QBitArray &mask):
_bitmaskFromInternal(mask) {}

const QBitArray &mask(){return _bitmaskFromInternal;}

virtual void doActionThatCrashes();

private:
const QBitArray &_bitmaskFromInternal;//!\warning Edit, thanks to d_stranz: That was an error in this code, since reference to stack variable was being copied. Remvoe ampresand.
};

void PropertyBitmaskConverter::doActionThatCrashes() {
QBitArray array = this->mask();
int size = array.size();
qDebug()<<"The number of bits is"<<size;
}


And the code itself is:



PropertyConverter *createPropertyConverter();
QBitArray bitArrayFromString(QString &array, bool *ok);
QBitArray bitArrayFromChar(const char *data, int size, bool *ok);


int main(int argc, char *argv[])
{
PropertyConverter *bitmaskConverter = createPropertyConverter();
if (0 != bitmaskConverter) {
//! \note Here it DOES crash
bitmaskConverter->doActionThatCrashes();
}

return 0;
}

PropertyConverter *createPropertyConverter()
{
bool ok;
QString mask = "b'11101'";
QBitArray bitMask;
if (!mask.isEmpty())
bitMask = bitArrayFromString(mask, &ok);
if (!ok || bitMask.isNull())
return 0;

PropertyConverter *bitmaskConverter = new PropertyBitmaskConverter(bitMask);

//! \note Here this doesn't crash
bitmaskConverter->doActionThatCrashes();

return bitmaskConverter;
}

QDebug operator<<(QDebug dbg, const QBitArray& z)
{
QString text;
for (int i = 0; i < z.size(); ++i)
text.prepend(z.testBit(i) ? "1": "0");
dbg << text;
return dbg;
}

QBitArray bitArrayFromString(QString &array, bool *ok)
{
return bitArrayFromChar(array.toLatin1().data(), array.size(), ok);
}

QBitArray bitArrayFromChar(const char *data, int size, bool *ok)
{
if (0 != ok)
*ok = true;

if ( ((strncmp(data, "B'", 2) == 0) || (strncmp(data, "b'", 2) == 0)) &&
data[size - 1] == '\'') {
const int significantDataSize = size - 3;
QBitArray result(significantDataSize, false);
const char *bitPtr = &data[2];
for (int i = significantDataSize - 1; i >= 0; --i) {
if (*bitPtr == '1')
result.setBit(i, true);
++bitPtr;
}
return result;
} else
if (0 != ok) *ok = false;

return QBitArray();
}



In the code attached the crash occurs on the streaming by QDebug, in the doActionThatCrashes(). When doActionThatCrashes() is called inside createPropertyConverter it works fine. When it's called one stack call higher (in the main() function), on the same object (same virtual call) it does nasty things.

In original application, the crash occurs on the array.size() call in PropertyConverter::doActionThatCrashes(). Please, if anyone could help me with this problem, I would be very glad. It stops me from further development and my fine grade :(

Best regards,
Krzysztof

Edit: Oh, I almost forgot. This could be important. I am using Qt 4.7.4 (64bit) on Linux (Ubuntu).

d_stranz
6th December 2011, 19:29
In line 21, you create a QBitArray on the stack. You then store a reference to that QBitArray inside your class. As soon as the createPropertyConverter method exits, that QBitArray goes out of scope and is destroyed, but your PropertyBitmaskConverter is still holding onto a reference to it. Kaboom!

Krzysztow
6th December 2011, 20:22
In line 21, you create a QBitArray on the stack. You then store a reference to that QBitArray inside your class. As soon as the createPropertyConverter method exits, that QBitArray goes out of scope and is destroyed, but your PropertyBitmaskConverter is still holding onto a reference to it. Kaboom!

Thank You for You input. However I disagree with You, maybe I'm wrong. Here is my reasoning...
Ok, QBitArray in 21st line is on stack. However its shared data gets created on heap. In the 27th line I pass a reference to the PropertyBitmaskConverter constructor, but in initialization list, calling _bitmastFromInternal(mask) means calling copy constructor of QBitArray (line 17th of the first code part). And, according to docs - it "constructs a copy of other". So when getting out of the function, mask is deleted, but shared data reference count is still non-zero, so this one stays and is kept for _bitmaskFromInternal member of PropertyBitmaskConver. So it doesn't go out of scope. Am I right?

Anyway thanks. If I am wrong, please correct me.

d_stranz
6th December 2011, 20:29
but in initialization list, calling _bitmastFromInternal(mask) means calling copy constructor of QBitArray

Wrong. All you are doing is storing a reference to the QBitArray, because your member variable (_bitmaskFromInternal) is also a reference. If you want to make a copy, declare _bitmaskFromInternal as a QBitArray, not a QBitArray &.

Krzysztow
6th December 2011, 20:36
Wrong. All you are doing is storing a reference to the QBitArray, because your member variable (_bitmaskFromInternal) is also a reference. If you want to make a copy, declare _bitmaskFromInternal as a QBitArray, not a QBitArray &.

Damn it, You are right! My bad. I meant this ampresand go to const QBitArray &PropertyBitmaskConverter::mask() method. I overlooked it, when trying o simplify my class. Yeah, I normally know that. And moreover, I don't have this error in my original class :/ So my problem still exists. But this code compiles fine now...

Thanks alot!

Edit: d_stranz, thank You even more! You know what? I did the same error in my code. I was so sure I haven't made such a mistake, that I was traversing hundreds of lines in search of other reason. And here it was. The thing is, I never store class references, not my style. In classes I use pointers :o I must really be tired.
Anyway, thank You so much! I can get back to work now.