Passing CPtrList from a dialog to an MDI Frame - c++

I have an application that builds a grid in an MDI frame from information gathered from a dialog beforehand, however multiple grids are built and I want to be able to select other grids (also built using the information gathered from the dialog) by selecting them from a combo box in the ribbon.
The combo box is populated when the MDI frame is first created, but when I try to select another grid the elements in CPtrList (Created with the dialog) has been deleted; I assume when the dialog is destroyed.
The code in CMainFrame is:
Cdialog dialog;
if (dialog.DoModal() == IDOK)
{
CFrame* pFrame;
BeginWaitCursor();
CMultiDocTemplate *pDoc = GetDocTemplate(10);
if (pDoc){
CBlankDoc* pDocument = (CBlankDoc*)pDoc->CreateNewDocument();
pDocument->SetTitle("Results");
pFrame = (CFrame*)pDoc->CreateNewFrame(pDocument, NULL);
if (pFrame)
{
pDoc->InitialUpdateFrame(pFrame, pDocument);
pFrame->m_plSplits = dialog.GetSplits();
pFrame->m_pParent = this;
pFrame->m_pMainRibbon = GetRibbonBar();
pFrame->MDIMaximize();
}
}
}
dialog.GetSplits();
looks as follows:
CPtrList* CDialog::GetSplits()
{
return &m_plSplits;
}
and
CPtrList* m_plSplits;

You return a pointer to an object that is defined inside the dialog.
If the pointer inside the CPtrList are still valid after the dialog has been destroyed you can create a copy of the pointer list. But in this case you also need to keep a list inside your frame. Currently you also save a just a pointer.
Another idea would be to create the pointer list and assign it to a smart pointer (use shared_ptr). Than you can transfer the smart pointer from the dialog and you can assign it to the frame object m_plSplits (this member must be a smart pointer too).
You have to construct this wisely and you have to determine the lifetime of the inner pointer... CPtrList doesn't destroy anything when the list gets destroyed.
Maybe just another containter would be a solution like std::list. This container can be copied.

Related

Segfault after deleting elements from vector

I'm currently working on a project, more precisely a tangram game.
I have a problem with a segfault and I don't understand why.
Given that I have a whole project, I will try to simplify the problem :
I have a GameManager class that contains in particular a Menu object (and other things, but I don't think that is important. The gameManager is used to inititialize this object and manage it.
The Menu contains a vector of Button(each button has a lambda to perform an action when the user click on it).
std::vector<std::unique_ptr<Button>> buttons;
To illustrate how it works I will take an example : if the user clicks on the "Load" button, the gameManager delete the current Buttons contained in the menu and add new buttons in that menu.
void GameManager::initMainMenuButtons() {
...
menu -> addButton(std::unique_ptr<Button>(new Button(x1, y1, x2, y2, "Create",
[this]{
std::cout << "Create level" << std::endl;
menu->clear()
initCreateLevelButtons();
actionManager->setMenu(menu);
}
)));
...
}
In that code sample, I have a method initMainMenuButtons that adds several buttons in the menu , like "Load" or "Quit".
When the user clicks on "Create", I want to change the interface (adding and deleting buttons). So, to delete the buttons, i call the method clear()
void Menu::clear() {
buttons.clear();
decorationPieces.clear(); // not interesting
}
I'm using unique_ptr, thus, I don't have to delete the buttons mannualy.
So far, no problem : the vector of buttons seems to be empty (size is 0).
Next, the method initCreateLevelButtons() is called. This method is very similar to initMainMenu : it adds buttons in the menu, nothing else. During this call, the buttons seems to be correctly added in the vector, I printed the content of the vector at the end and the vector contains the correct buttons.
And there, the problem appears : after the call of initCreateLevelButtons(), there is a segfault when i want to use the menu, so, actionManager->setMenu(menu); doesn't work. I tried to print the menu std::cout << menu << std::endl, and test if this pointer is nullptr, but it doesn't work either. I don't understand why the menu seems to be correct at the last line of initCreateLevelButtons() and becomes invalid just after.
If I doesn't clear the vector of buttons (the menu->clear instruction), the program works, but, the last buttons are still here).
I tried to use raw pointers and I notices that the program is able to clear the vector as long as the buttons are not deleted (If I add a loop to delete the buttons, the problem arises), so, I conclued that the probleme is the buttons deleting. I don't understanf why, I'm stuck.
I don't know if I explained it weel, because, as I have already said, the code is part of a whole project, it's hard to introduce classes without introduce other things.
if you want details or the complete code of methods, I can provide them.
menu sustains lifetime of some button
button sustain lifetime of lambda
when you click button lambda clears menu
menu destructor clears button, button clears lambda
lambda continues execution when it in fact has been already destroyed -> undefined behavior ends with a crash
Now question is: Do you own Button class?
If yes then the easiest way to fix it, is to invoke copy of lambda in the button.
When you call menu->clear() it calls buttons.clear().
When you call buttons.clear() it destroys all the elements of buttons.
When you destroy the unique_ptr for the "Create" button, it destroys the "Create" button.
I assume button's callback is a std::function. When the button is destroyed, so is the std::function.
When the std::function is destroyed, your callback lambda object ([this]{...}) is destroyed.
The this pointer inside a lambda is stored in the lambda object. So now the memory that held the this pointer has been deallocated.
Since actionManager is a member variable of GameManager, actionManager->setMenu(menu) is really this->actionManager->setMenu(menu) which crashes because it uses a dangling pointer.
One workaround is to put the button code in a function of GameManager (since the GameManager is not destroyed), and call that from the lambda. Then, it's okay if you destroy the button while inside that function. It's okay to destroy an object whose code is currently running, as long as you are careful to not access the object after it's destroyed! This is also okay with std::function. I.e.:
[this]{
// move the rest of the code to the CreateLevel function
this->CreateLevel();
// At this point the lambda has been destroyed, but it's not a problem
// because we don't do anything.
}

Qt Parent to delete variable after child has closed

I'm writing an application in C++ using the Qt library. There is a central window (the parent) and all the children are launched when needed. Since a number of these windows can be open multiple times, but display different data, I'm declaring the objects with new. Here is an example of what I've got:
Home_Window.hpp
View_Window *SomeWindow;
Home_Window.cpp
void Home_Window::on_WindowButton_clicked()
{
SomeWindow = new View_Window();
SomeWindow->show();
}
What I want to do, is delete the object, when the window is closed to reduce the memory footprint of the application. I've already been able to delete all of the objects contained in the child window to reduce memory usage, but the core object still lingers, and if, through a single day a user opens and closes 1000's of windows, and each object holds onto 40-50KB of memory, the footprint of the application goes from a couple of MBs of memory to 100's of MBs of Memory.
I've not been able to find a guide online that would allow me to achieve this. I was considering a slot and signal pair, as I know that when a window is closed, it sends the QObject::destroyed() signal. Only issue, is I've not tried to setup a signal and slot pair like this.
Any suggestions are appreciated.
to delete the window when it is closed, you can set the WA_DeleteOnClose attribute on it. Your on_WindowButton_clicked() should look something like:
void Home_Window::on_WindowButton_clicked()
{
View_Window* w= new View_Window();
w->setAttribute(WA_DeleteOnClose);
w->show();
}
This way, you don't have to worry about destroying w yourself, it will get deleted automatically when it is closed.
You need to do two things:
The window's lifetime must be managed even if the window isn't closed.
You can give it a parent that you know will end its life at some point. Or you can use a QScopedPointer or std::unique_ptr.
The window must delete itself when it's closed.
void Home_Window::on_WindowButton_clicked()
{
// parent window flags
// vvvv vvvvvvvvvv
auto window = new View_Window(this, Qt::Dialog); /* 1. */
window->setAttribute(Qt::WA_DeleteOnClose); /* 2. */
window->show();
}
At the very least, you should set the Qt::Window flag. The Qt::Dialog includes the Qt::Window flag, and also declares the window to be a dialog box - that fact has platform-specific interpretation; read more about window flags here.
If your View_Window's API is broken, and it doesn't take the window flags as the 2nd argument to the constructor, you need to set them separately:
window->setWindowFlags(Qt::Dialog);
Every widget should be taking Qt::WindowFlags as the optional, 2nd argument to the constructor, with a default value reflecting the usual use of the window. That's the expectation set by all of Qt's widgets.
You can try to delete the pointer to ViewWindow by using: delete SomeWindow;

valgrind issue with QDialog specified with qt designer

I'm developing a simple prototype with qt creator.
I have used designer in order to design my windows.
Say us that main window has a menu with an option called "Suspend". When this option is selected, it is called the method MainWindow::on_actionSuspend_triggered() whose simplified implementation could be resumed as follows:
void MainWindow::on_actionSuspend_triggered()
{
SuspendDialog suspend_dialog(this);
suspend_dialog.setModal(true);
auto status = suspend_dialog.exec();
return;
}
The SuspendDialog was specified with designer, it is derived from QDialog class and it is very simple (three push buttons a combo box and a spin box. This class does not allocate memory.
Now, when I run valgrind inside qtcreator for checking memory usage I get two issues of type Mismatched free() / delete / delete []. Some bizarrus is that the two issues refers the same line, which is at the end of destructor of SuspendDialog whose implementation is:
SuspendDialog::~SuspendDialog()
{
delete ui;
}
And that was automatically generated by qt designer.
My question is: is this a false positive of valgrind or am I doing some wrong?
Thanks in advance
By doing the below you are asking for trouble:
SuspendDialog suspend_dialog(this); // wrong! do not pass 'this' here
Passing pointer to 'this' in Qt implies that you pass the parent responsible for release of that widget. Or, the release will happen twice: first when the object on stack getting destroyed and next when the parent object getting destroyed.
If you execute the dialog with exec() you can still allocate the dialog widget on stack but do not pass this to it:
SuspendDialog suspend_dialog;
//
suspend_dialog.exec(); // exec() only
Or you can allocate the dialog widget in the heap and then you can pass this to it:
SuspendDialog* pSuspendDialog = new SuspendDialog(this);
//
pSuspendDialog->exec(); // or maybe show() depending on task

How can I change the default CDialog font for a non-modal dialog?

It is necessary to switch off the "ClearType" property of the default font for all dialog controls. It is possible to do that for one control by setting
logfont.lfQuality = ANTIALIASED_QUALITY
There are a lot of suggestion how to do the same for modal dialogs (http://neelaakash.wordpress.com/2007/12/31/change-default-dialog-font-of-cdialog/ and others), but that should be done for non-modal dialogs (are instantiated with new and Create(...) methods). I've tried to do that myself:
Override 'Create' method, and modify dialog template:
BOOL CActivationChildDialogLicenseInfo::Create(UINT nIDTemplate,
CWnd* pParentWnd)
{
CDialogTemplate dlt;
int nResult;
// load dialog template
if (!dlt.Load(MAKEINTRESOURCE(nIDTemplate))) return -1;
// set your own font, for example “Arial”, 10 pts.
dlt.SetFont(L"Arial", 12);
// get pointer to the modified dialog template
LPSTR pdata = (LPSTR)GlobalLock(dlt.m_hTemplate);
// let MFC know that you are using your own template
m_lpszTemplateName = NULL;
InitModalIndirect(pdata);
// display dialog box
nResult = CActivationChildDialog::Create(nIDTemplate, pParentWnd);
// unlock memory object
GlobalUnlock(dlt.m_hTemplate);
return nResult ;
}
Seems like this method do nothing (it is called, I've checked that with putting break-point inside).
I've tried to call
nResult = CActivationChildDialog::Create(NULL, pParentWnd);
...but got a lot of ASSERTs.
I've also tried to override the 'OnSetFont' method:
void CActivationChildDialogLicenseInfo::OnSetFont(CFont *pFont)
{
CActivationChildDialog::OnSetFont(pFont);
LOGFONT logfont;
pFont->GetLogFont(&logfont);
LOGFONT logfont2=logfont;
pFont->DeleteObject();
logfont2.lfItalic = true;
logfont2.lfQuality = ANTIALIASED_QUALITY;
pFont->CreateFontIndirect(&logfont2);
}
That causes ASSERT during run-time and resulted in a VERY big font being used (lost default font settings, doesn't accept new specified settings)... I don't know why.
Please advise, how can I change a default dialog font that will be "inherited" by all dialog controls?
Thank you very much.
First off: the simple, reliable way to do this is to create the dialog and then send WM_SETFONT (or call SetFont()) to the dialog and each control in it. I'll show you how to do that below, but first, here's why the two strategies you've already tried didn't (and can't) work:
Modifying the dialog template
First off, you should be calling CDialog::CreateIndirect() if you wish to use a dialog template that you've already loaded.
But don't bother. The dialog template contains only the face name and point size - it does not allow you to specify other LOGFONT values such as lfQuality. If it did, you could simply specify that in your resource definition and avoid writing any runtime code at all!
Intercepting WM_SETFONT
In theory, you could make this work. But it's not practical. Your code has several problems: first off, you'd have to intercept this message for every child control in order for it to do anything useful: the dialog itself probably doesn't render any text. But worse, you're passing the original font to the base class (which hands it to the default window procedure, which stores it internally for later use) and then immediately destroying it - this means the dialog (and everything else using that font, including all of the child controls) will be trying to draw text using a bogus font, and reverting to the default font as a result. Finally, you're creating a new font attached to a temporary object (pFont) created and destroyed by MFC - internally, the CFont object you're working with will be detached from the font handle and destroyed, leaking a handle to a font object that nothing uses.
Leaky abstractions: a note on HFONT and CFont
HFONT is a handle type that Windows uses to represent a font object. Like most of GDI, there are specific functions for creating fonts, and the general-purpose DeleteObject() function for destroying them.
CFont is a light-weight wrapper class for HFONTs. A CFont instance can be attached and detached from an existing HFONT, or used to create a new one. If a CFont instance is still attached to a HFONT when its deconstructor executes, it will call DeleteObject() to destroy the underlying object. Internally, MFC utilizes temporary CFont instances that are attached and detached from HFONTs when calling various message handlers (such as OnSetFont). It's worth remembering that internally, Windows knows nothing about CFont, and a single HFONT may belong to 0 or more CFont instances at any given point in time.
A note on fonts and WM_SETFONT
When you create a new font - whether or not it is wrapped in a CFont object - you are the owner of that font, and it is your responsibility to destroy it once you are finished using it. Passing it to WM_SETFONT (CWnd::SetFont()) doesn't change ownership! This is actually quite useful, as it allows you to pass the same font to multiple windows without worrying about which one will destroy it - you're still the owner, and so you can (and must) destroy it yourself (once there are no windows still using it).
Finally - how to quickly create and set a font on a dialog and all its children
So you should now have enough background to understand the necessary steps:
Create the dialog
Create the desired font
Pass the font to the dialog and its children (by either sending WM_SETFONT messages, or by calling CWnd::SetFont... which itself send a WM_SETFONT message).
When the dialog is destroyed, also destroy your font.
Example
// define this as a class member - class destructor then handles step four!
CFont m_nonCleartypeFont;
BOOL CActivationChildDialogLicenseInfo::Create(UINT nIDTemplate,
CWnd* pParentWnd)
{
// step one: create dialog normally
BOOL nResult = CActivationChildDialog::Create(nIDTemplate, pParentWnd);
// step two: create custom font
// relying on destructor to destroy font once we're done with it
// so be careful to only create it once!
if ( NULL == m_nonCleartypeFont.m_hObject )
{
CFont* pOriginalFont = GetFont(); // use template font as... template!
// pull information from original font
LOGFONT logfont;
pOriginalFont->GetLogFont(&logfont);
// make font adjustments:
// specify italics
logfont.lfItalic = true;
// and non-cleartype antialiasing
logfont.lfQuality = ANTIALIASED_QUALITY;
// create our font based on adjusted information
m_nonCleartypeFont.CreateFontIndirect(&logfont);
}
// step three: set our custom font on the dialog and all children
SetFont(&m_nonCleartypeFont, FALSE);
// Send message to quickly set this font for all children.
// See documentation for SendMessageToDescendants()
// - this is actually the example given!
SendMessageToDescendants(WM_SETFONT,
(WPARAM)m_nonCleartypeFont.m_hObject,
MAKELONG(FALSE, 0),
FALSE);
return nResult;
}

MFC: Deleting dynamically created CWnd objects

Lets say in a dialog, we dynamically create a variable number of CWnds... like creating a and registering a CButton every time the user does something/
Some pseudo-code...
class CMyDlg : public CDialog
{
vector<CWnd *> windows;
void onClick()
{
CButton *pButton = new CButton(...);
//do other stuff like position it here
windows.push_back(pButton);
}
}
Do I need to explicitly delete them or will MFC do it? If I have to, would it be in the destructor as normal, or are there any special things to avoid breaking MFC... making sure I don't delete the objects while the HWNDs are still in use for example?
CButton *pButton = new CButton(...);
These are C++ objects, which needs to be deleted explicitly. (Where as Main frame windows and Views are self destructed).
You can refer the detailed answer ( by me) Destroying Window Objects