Destructor randomly throw EXC_BAD_ACCESS
-
Hello everybody !
I'm making an app for a company, and I'm already stuck on the first window T_THere is what I want to do: I have a "MainWindow" class which inherits "QMainWindow". It has a method called "buildWelcomeWidget" which init an internal "WelcomeWidget" widget (inherits QWidget) and assign it as the centralWidget.
Talking about it, the WelcomeWidget has internal "signinWidget" and "signupWidget" which are built and called on a QPushButton click.
My main.cpp init the MainWindow, call buildWelcomeWidget and show the MainWindow, which works perfectly fine. The problem is when I close the widget (with the red cross button), it calls the destructor or the MainWindow, which deletes the welcomeWidget, and there is a EXC_BAD_ACCESS in the destructor of my welcomeWidget, which deletes every pointers I'm using but in the right order (I delete the buttons, then the layouts, then the widgets, no matter if they've been init or not).
I think it's fine to delete a pointer 2 times, but not if it's NULL, right ?
Here is some extracts:
main.cpp
@int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow mw;mw.buildWelcomeWidget();
mw.show();return (a.exec());
}@MainWindow.cpp
@
MainWindow::~MainWindow()
{
delete this->_welcomeWidget;
}void MainWindow::buildWelcomeWidget(void)
{
this->_welcomeWidget = new WelcomeWidget(this);
this->setCentralWidget(this->_welcomeWidget);
}@WelcomeWidget.cpp
@WelcomeWidget::WelcomeWidget(MainWindow *parent) : QWidget(parent)
{
this->_vboxLayout = new QVBoxLayout(this);
this->_signinButton = new QPushButton(tr("Sign-In"), this);
this->_signupButton = new QPushButton(tr("Sign-Up"), this);this->_vboxLayout->addWidget(this->_signinButton);
this->_vboxLayout->addWidget(this->_signupButton);
this->setLayout(this->_vboxLayout);
this->setAttribute(Qt::WA_MacBrushedMetal);connect(this->_signinButton, SIGNAL(clicked()), SLOT(signinClicked()));
connect(this->_signupButton, SIGNAL(clicked()), SLOT(signupClicked()));
}WelcomeWidget::~WelcomeWidget()
{
delete this->_cancelSigninButton; // Fails randomly here
delete this->_cancelSignupButton;
delete this->_okSigninButton;
delete this->_okSignupButton;
delete this->_signinButton;
delete this->_signupButton;
delete this->_usernameSigninField; // Fails randomly here
delete this->_usernameSignupField;
delete this->_passwordSigninField;
delete this->_passwordSignupField;
delete this->_password2Field;
delete this->_emailField;
delete this->_firstnameField;
delete this->_lastnameField;
delete this->_emailValidator;
delete this->_hboxSigninLayout;
delete this->_hboxSignupLayout;
delete this->_vboxLayout;
delete this->_formSigninLayout;
delete this->_formSignupLayout;
delete this->_signinWidget;
delete this->_signupWidget;
}@Is there an explanation somewhere :) ?
Thank you for your help. -
[quote author="Lukas Geyer" date="1342024242"]Why do you delete the widgets in the first place? They are deleted by Qt automatically.
[/quote]
I'm deleting the widget to respect the rule "1 new, 1 delete", even if it's deleted automatically, is it wrong ?[quote author="Lukas Geyer" date="1342024242"]
You can delete a null pointer, but you are not allowed to delete an object twice, never.[/quote]
Okay okay, I was wrong. In my destructor, I'm deleting QWidgets that have been initialized, and non initialized. Are you saying I shouldn't ?What should I do then ?
-
If you create a QWidget (or, rather, a QObject) which has a non-null parent, then you shouldn't need to delete it yourself. The parent-child hierarchy of QObjects take care of deleting these objects for you. (Giving an object a parent hands ownership of that object to its parent, who will clean it up.)
With that said, if you do delete any of these objects manually, then you need to set their pointers to 0 (NULL) after you've deleted them. Clearing a pointer to null after a delete is a always good practice, regardless.
-
[quote author="Max13" date="1342024765"]I'm deleting the widget to respect the rule "1 new, 1 delete", even if it's deleted automatically, is it wrong?[/quote]It isn't wrong, but it's quite unfamiliar in Qt, especially when it comes to QObject, where you usually either rely on ownership (as explained by mlong) or smart pointers to delete objects.
[quote author="Max13" date="1342024765"]Okay okay, I was wrong. In my destructor, I'm deleting QWidgets that have been initialized, and non initialized. Are you saying I shouldn't?[/quote]
I'm not quite sure what you mean exactly with 'initialized', but you are only allowed to <code>delete</code> an object (exactly once) which has been created using <code>new</code>, or a null object (be aware that this is only true for C++, not C). Everything else will result in undefined behaviour.
@
QObject objectA;
QObject *objectB;
QObject *objectC = 0;
QObject *objectD = new QObject;
QObject *objectE = new QObject(objectD);delete &objectA; // WRONG, not created using new (stack object)
delete objectB; // WRONG, object neither initialized nor null
delete objectC; // RIGHT, null object
delete objectC; // RIGHT, null objectdelete objectD; // RIGHT, initialized object created using new
delete objectD; // WRONG, object already deleteddelete objectE; // WRONG, object already deleted due to beeing child of objectD
@
Keep always in mind that not a pointer is deleted, but rather the object it points to! -
[quote author="mlong" date="1342033053"]If you create a QWidget (or, rather, a QObject) which has a non-null parent, then you shouldn't need to delete it yourself. [...] With that said, if you do delete any of these objects manually, then you need to set their pointers to 0 (NULL) after you've deleted them.[/quote]
Ok. Thank you for your explanation, I'm not as advanced in C++ (low level) that I am in C.[quote author="Lukas Geyer" date="1342037269"]I'm not quite sure what you mean exactly with 'initialized', but you are only allowed to <code>delete</code> an object (exactly once) which has been created using <code>new</code>, or a null object (be aware that this is only true for C++, not C). [...] Keep always in mind that not a pointer is deleted, but rather the object it points to![/quote]
By "initialized", I meant "newed". I thought (I should have verified) that Qt doesn't set a pointer to 0/NULL on a <code>delete</code>.Something strange, is that I've tried something that cause an EXC_BAD_ACCESS too:
@this->_welcomeWidget = new WelcomeWidget;
delete this->_welcomeWidget;@This should work isn't it ?
When entering the <code>delete</code> (<code>~WelcomeWidget()</code>) it fails at the first child <code>delete</code> (the destructor is the same as on my first post).
Is it for the same reason explained by mlong? Because I've understood that if I delete a parent QWidget that has a QPushButton child, I won't be able to delete the QPushButton. But I've tested to delete the QPushButton inside the QWidget destructor, shouldn't it work ?Thank again for your lights.
-
[quote author="Max13" date="1342044793"]I thought (I should have verified) that Qt doesn't set a pointer to 0/NULL on a <code>delete</code>.[/quote]
It (C++, not Qt) doesn't, because it can't. The pointer and the object it points two are two different things, with two different addresses in address space, occupying two different areas of memory.
If you pass a pointer to a function (or <code>delete</code>) you will always pass the address of the object it points to (the 'value' of the pointer), not the address of the pointer itself (which would be required to modify it).
@
void deleteObject(QObject *objectToDelete)
-----------------------
0x28fe30
{
delete objectToDelete;
--------------
0xfd7e738
}QObject *object = new QObject;
0x28fe94 0xfd7e738
deleteObject(object);
@
As you can see, <code>deleteObject</code> cannot modify the pointer called <code>object</code>, because it has no access to it. You could theoretically do something like <code>delete 0xfd7e738;</code>, then there is no named pointer involved at all.[quote author="Max13" date="1342044793"]Something strange, is that I've tried something that cause an EXC_BAD_ACCESS too:
@
this->_welcomeWidget = new WelcomeWidget;
delete this->_welcomeWidget;
@
[/quote]
Yes, because you <code>delete</code> a load of objects you haven't created using <code>new</code> nor are the pointers which point to them are <code>0</code>, for example <code>cancelSigninButton</code>.Pointers (as other variables) are never initialized automatically (with a few exceptions, see 4.9.5). The contain a random value unless explicitly set (to for example <code>0</code>).
On a sidenote: identifiers (ie. variable names) should not start with an underscore, as they are (in most situations) reserved to the implementation (ie. compiler or standard library). See 7.1.3 and 17.4.3.2.1 of the C++ standard for further reference.
[quote author="Max13" date="1342044793"]Because I've understood that if I delete a parent QWidget that has a QPushButton child, I won't be able to delete the QPushButton. But I've tested to delete the QPushButton inside the QWidget destructor, shouldn't it work ?[/quote]
If you delete a child it is automatically removed from the parents child list. If you delete a parent, all children are automatically deleted as well. This means, that all pointers to child objects now point to objects in memory which have been already deleted, and you are not allowed to delete them twice. -
I havn't thought about all of this...
I know these things about C++, about pointers specificities (address of the pointer, and address it points to).
Thank you for all your information. I think I will overload delete or add a "deleteObject" on QObject, using pointer reference and which will <code>delete</code> a pointer then = NULL. This is the default behavious I personnaly need on every project I'm developing, since I don't need the old address where the pointer was equal to.
Thanks again for your help.
-
I do not recommend doing so, because it is just an alleged saftey net, which will stab you in the back once you rely on it.
Keep in mind that there can be an arbitrary number of pointers referencing the same object, not just the one used to delete or create the object.
@
QObject *object = new QObject;
QObject *sameObject = object;QObject *anotherObject = new QObject(object);
deleteAndNullifyPointer(&object);
deleteAndNullifyPointer(&sameObject); // BANG, already deleteddeleteAndNullifyPointer(&anotherObject); // BANG, already deleted
@
There is just a single solution to memory management in C/C++: having a well-defined model when objects are created and deleted and who is responsible for that.There are two automatisms in Qt that will help you here. The ownership mechansim already explained and "smart pointers":http://labs.qt.nokia.com/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/, which are kind-of what you are trying to implement.