I have a class MainWindow
derived fromQMainWindow
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
struct MetaObra
{
InterfazObra* miobra;
QString nombrefichero;
MetaObra():miobra(nullptr){}
};
std::list<MetaObra>ListaObras;
std::list<MetaObra>::iterator obraActual;
.........................
}
When I add an element of type MetaObra to the list, a tab of a QTabWidget
appears. When I change the active tab, the iterator points to the MetaWork of the list... so far so good. I have the problem when closing tabs, if I close a tab other than the last one, the iterator goes to the next tab, but when I want to close that one, the program crashes and closes.
This would be the code to close the tabs:
void MainWindow::ActionCerrar()
{
std::list<MetaObra>::iterator obraBorrar = obraActual;
if (ListaObras.size()>1)
{
if (obraActual!=ListaObras.end() && std::next(obraActual)==ListaObras.end())//ultimo elemento
{
obraActual=std::prev(obraActual);
}
else
{
obraActual=std::next(obraActual);
}
}
delete obraBorrar->miobra;
ListaObras.erase(obraBorrar);
}
What I do first is check if there is more than one element in the list. If so, I check if it's the latest. If it's the last one I put the iterator on the previous element. If it is not, I put it in the next element. Then I proceed to delete the content of the pointer and finally remove the element from the list. I have checked the behavior and it is correct.
Imagine that I have 3 works, one in each tab. Work1, Work2, Work3. If I close the Obra2 tab, Obra3 is put as the active tab. If I close it then, the program fails. However, if before closing the tab I switch to Work1 as the active tab and then go back to Work3, it closes normally. When I close the tabs in descending order there is no problem, but if I close them from "left to right"
It's simpler than what you're trying.
std::list::erase
returns an iterator to the next element in the list. The idea then is to delete the element yes or yes and check if the returned iterator is the end of the list. If that happens and it turns out that the list has elements, just move said iterator back one position to get the iterator to a valid tab:Of course, consider implementing a destructor for the structure and be able to avoid the
delete
:Edit: The above solution can give problems if the elements are inserted into the list by copy (a possible example):
Since with this design, the constructor copy of would be called to
MetaObra
copy the object in the container and later the object would be destroyedobra
, which would release the corresponding resources.There are broadly two solutions:
Use pointers in the list. This eliminates object copies and object destruction, but forces dynamic memory to be used:
Use the move syntax :
If the above doesn't work at first you may need to enable the move constructor . Since it should not be necessary to give it a specific functionality at first, it could be left with its default implementation:
The great advantage of this solution is that you avoid resorting to dynamic memory... the great disadvantage is that you have to be careful not to make copies of the object that could destroy the pointer... perhaps it would be convenient to disable the copy constructor and the operator Assignment:
This will prevent the compiler from creating copies of the objects. Keep in mind that for specific uses you can always resort to references:
For more information about the move syntax you can refer to the following link
Using smart pointers (similar to the first solution but without dealing with dynamic memory directly):
This solution allows you to create copies of smart pointers. Now the object will only be destroyed when no smart pointer is referencing it.
The disadvantage of this solution is that if you are not very careful with the use of pointers you can create memory leaks.
Well, this would be the adapted code of the function, according to @eferion's answer: