Cleaning up QList and QGraphicsScene to avoid memory leaks - c++

I want to be thorough and clean up to avoid memory and object leaks.
I had the misunderstanding that Qt cleans automatically objects that go out of scope, except pointers need to be deleted manually.
In my code I have a number of QList... example:
void someFunction()
{
QList<int> x = QList<int>() << 0 << 0 << 0 << 0;
QList<QImage> y; // filled in some way
QList<QGraphicsScene*> s;
s = otherFunction(x, y);
}
QList<QGraphicsScene*> otherFunction(QList<int>& x, const QList<QImage>& y)
{
QList<QGraphicsScene*> s;
s.push_back(this->scene()); // a few times; of course there is some processing...
return s;
}
void thirdFunction()
{
QList<int> x = QList<int>() << 0 << 0 << 0 << 0;
QList<QImage> y; // filled in some way
QList<QGraphicsScene*> s;
s = otherFunction(x, y);
for (int i = 0; i < s.size(); ++i)
{
view[i]->setScene(s.at(i)); // view is a QList<QGraphicsView*>
}
}
Called multiple times, I can see the memory increasing (seen from task manager).
Obviously when items go out of scope, they are not being deleted... I immediately suspected the list of scene pointers.
1) What is the best way to delete them ? Will
qDeleteAll(s);
in someFunction() be enough ? Will it delete the scene pointers, as well as all the QGraphicItems inside the scenes ? or do I need to iterate through the list of scenes, and delete all items ? Do I have to do this :
for (int i = 0; i < s.size(); ++i)
{
s.at(i).clear();
} // then
qDeleteAll(s);
2) Do I need to delete the lists of simple variables (int, bool) ?
What about lists of objects, like QImage ?
3) I assumed that removing an item from the scene deletes its pointer. But now I read that, after removing an item from the scene, it needs to be deleted manually. So does
scene().clear();
delete the QGraphicItem pointers that have been added to the scene ? Or should the items be deleted as well using a delete call ?
4) In the thirdFunction, do I have a memory leak ? It seems like I do !!
I can't delete the scenes since I set them onto views... but what happens to the scenes that were assigned to the views already ?
How do I clean that correctly ?

1.
qDeleteAll deletes items in a container using the C++ delete operator. It's also stated in the Qt documentation about QGraphicsScene destructor:
Removes and deletes all items from the scene object before destroying
the scene object
So it's enough to call qDeleteAll(s).
2.
There is no need to delete lists of basic or Qt types like QList<int> or QList<QImage> as they are deleted when the list is deleted.
3.
QGraphicsScene::clear() removes and deletes all items from the scene. So calling scene().clear(); is enough. It's equivalent to calling :
qDeletaAll( scene()->items() );
4.
Since a view does not take ownership of scene, you should delete the previously assigned scenes using deleteLater() :
for (int i = 0; i < s.size(); ++i)
{
view[i]->scene()->deleteLater();
view[i]->setScene(s.at(i)); // view is a QList<QGraphicsView*>
}

Related

c++ 2d array of class pointers

i am trying to create a 2d array holding pointers of my class. first, i'd like to assign all of them NULL:
Timetable::Timetable(int hours) : TimetableBase(hours){
scheduledLectures = new Lecture**[5];
for (int i = 0; i < 5; i++) {
scheduledLectures[i] = new Lecture*[hours];
for (int j = 0; j < hours; j++)
scheduledLectures[i][j] = NULL;
};
}
this is for a timetable generator application. i have a function to set these pointers to a specific object.
void Timetable::setLecture(Lecture& lecture){
while ((lecture.getDuration()) -1 > 0){
scheduledLectures[lecture.getDayScheduled()][(lecture.getHourScheduled())+1] = &lecture;
}
}
the compiler returns no errors for this, but when its running, it seems that the pointers remain NULLs.
i am sure the error is inside the setter function (and almost sure that its a grammar mistake) but i cannot find the solution for that.
whats wrong in here?
thank you
Use a vector (or std::array) of pointers or shared_ptrs (or unique_ptrs depending on how your lifetimes are arranged) instead of a 2D array of pointers that you manage yourself. Save yourself the trouble of managing the memory and lifetimes of your objects manually.
class TimeTable {
vector<vector<shared_ptr<Lecture>>> scheduledLectures;
};
Timetable::Timetable(int hours)
: TimetableBase(hours),
scheduledLectures(5, vector<shared_ptr<Lecture>>(5)) {}
void Timetable::setLecture(std::shared_ptr<Lecture> lecture){
while ((lecture->getDuration()) -1 > 0) { // not sure what this does
scheduledLectures[lecture->getDayScheduled()][(lecture->getHourScheduled())+1] = lecture;
}
}
You can test whether a shared_ptr is null like follows
auto s_ptr = std::shared_ptr<int>{}; // null
// either assign it to a value or keep it null
if (s_ptr) {
// not null
}
If you are managing the memory of the Lecture objects elsewhere then just use a 2D vector of pointers and trust your code
class TimeTable {
vector<vector<Lecture*>> scheduledLectures;
};
Timetable::Timetable(int hours)
: TimetableBase(hours),
scheduledLectures(5, vector<Lecture*>(5)) {}
void Timetable::setLecture(Lecture& lecture){
while ((lecture.getDuration()) -1 > 0) { // not sure what this does
scheduledLectures[lecture.getDayScheduled()][(lecture.getHourScheduled())+1] = &lecture;
}
}

Values being overwritten in array of pointers

I'm Java guy trying to solve discrete knapsack problem in c++. However, I'm having trouble with pointers. I have an object with a field
Item ** items;
representing array of items to choose from. I also created a method to add an item which works like insertion sort (at least I hope so).
void Knapsack::addItem(Item item) {
int k = itemCount - 1;
if (this->items[k] != NULL) {
return;
}
while (k > 0 && this->items[k - 1] == NULL) {
k--;
}
if (k == 0) {
this->items[0] = &item;
} else {
int i = 0;
while (i < k && item < *(this->items[i])) {
i++;
}
for (int n = k; n > i; n--) {
this->items[n] = this->items[n - 1];
}
this->items[i] = &item;
}
}
Later, in my main I invoke the method by
knapsack->addItem(*(new Item(values.at(0), values.at(1))));
values being a vector of ints. The method itself seems to work fine, however, debugger shows that everytime I invoke the method with new Item, the previous values already put in my array are set to the same values as the new item.
(ex. if items[0] has value of 5, and I invoke the method with an item valued as 10, the items[0] instantly is set to 10).
Why are the values overwritten? I am creating a new object everytime I invoke the method.
EDIT:
Problem was fixed by replacing
this->items[0] = &item;
this->items[i] = &item;
with
this->items[0] = new Item(item.getWeight(), item.getValue());
this->items[i] = new Item(item.getWeight(), item.getValue());
SECOND EDIT:
The answer shows better (and probably correct) way to do this. Now the function takes a pointer instead of an object.
void Knapsack::addItem(Item * item);
this->item[i] = item;
knapsack->addItem(new Item(values.at(0), values.at(1)));
You are storing a pointer to a temporary copy of an Item object in the array in your addItem function, once the function returns the temporary object will be destroyed and you will be left with an invalid pointer. Make sure to allocate an Item object on the heap and passing a pointer to your addItem function or just use a vector of type std::vector<Item> and save your objects in there.

I am stuck with copying a class given an address, I am segfaulting

I have struggled with it for a while but I really can't get it, I am just getting segfaults. I am trying to copy a class, the function I am writing to copy is also below. Crossed out are combinations that I have tried in vain, it's time to call for help
class Scene
{ private:
int max;
int* x_row, *y_col; // maximum and min coordinates of each image
Image**image_layers;
}
void Scene::_copy(const Scene &source)
{
max = source.max;
x_row = new int[source.x_row];
y_col = new int[source.y_col];
image_layers = new Image*[source.max];
for(int i = 0; i < source.max; i++)
{
if(source.image_layers[i] != NULL)
{
//image_layers[i] = new Image(*(source.image_layers[i]));
// image_layers[i] = new Image;
//*image_layers[i] = *source.image_layers[i];
// image_layers[i] = source.image_layers[i];
}
else
{
image_layers[i] = NULL;
}
x_row[i] = source.x_row[i];
y_col[i] = source.y_col[i];
}
EDIT:
I forgot to say that this function is called as " scene(*set) "
The segfault happens here or because of this:
x_row = new int[source.x_row];
y_col = new int[source.y_col];
On the right hand side, you use the address source.x_row as an array size. This is a very large number that most likely will cause the allocation to fail.
You need to keep a member for holding the size or better yet use a std::vector<int> object instead.
Copying C arrays are done faster with memcpy. With C++ vectors, you can just assign one to the other:
x_row = source.x_row
Nothing to do with the question, but this function should be named operator=, will make using the class easier by assigning one instance to another:
Scene & Scene::operator=(const Scene &source)
{
// copy elements
...
return *this;
}
x_row = new int[source.x_row];
y_col = new int[source.y_col];
Are you sure about above code?
source.x_row is pointer

Deleting a dynamically allocated object inside of an object inside of an array

I have a destructor that looks like this:
Tilemap::~Tilemap(void)
{
if(width>0 && height > 0)
{
for (int x = 0; x < getMapWidth(); x++)
{
for (int y = 0; y < getMapHeight(); y++)
{
if(getTileAtPoint(x,y)->isInteractable())
{
delete getTileAtPoint(x,y)->getInteractable();
}
}
}
SAFE_DELETE_ARRAY(tileArray);
}
return;
}
SAFE_DELETE_ARRAY() is just a macro that looks like this:
#define SAFE_DELETE_ARRAY(ptr) { if(ptr) { delete [](ptr); (ptr)=NULL; } }
The tileArray is a dynamically allocated array that contains tiles which contains interactable objects. Its declaration looks like this:
tileArray = new Tile[mapWidth*mapHeight];
Whenever I delete the double for-loop section of code and just leave this:
Tilemap::~Tilemap(void)
{
if(width>0 && height > 0)
{
SAFE_DELETE_ARRAY(tileArray);
}
}
I get memory leaks because there is an interactable object that I declared that is not getting deleted. However, when I leave the double for-loops, the code makes it through the for-loops just fine but then crashes on the SAFE_DELETE_ARRAY with this error message:
Unhandled exception at 0x008927CC in Spaceship.exe: 0xC0000005: Access violation reading location 0xFEEEFEEE.
Does anyone know a way to fix this please?
A very simple pattern: have the destructor of your tile objects delete their Interactable objects if they have them. That way deleting a tile cleans up automatically and you don't have to worry about this on the outside.
Or use some type of smart pointer instead of a raw pointer for the objects referenced by your Tile class.

c++ Dynamic 2d array not using default constructor

I got a problem that is beyond my knowledge. I'm working on a HGE project, but this is a c++ issue, not the HGE engine itself.
The question:
I want to create a 2D array with 3 different animations on 5 different turtles. However, I need to use a constructor in HGE something like this;
turtleAnim[i] = new hgeAnimation( turtleTexture, 6, 6, 0, 0, 110, 78 )
But I don't know how to do it! All examples on the interwebz handle the problem as if it got a default constructor. Something like this:
in class:
#define TURTLECOUNT 5
#define TURTLEANIMATIONS 3
private:
hgeAnimation** turtleAnim;
in the.cpp
turtleAnim= new hgeAnimation*[TURTLECOUNT];
for(int i=0; i<TURTLECOUNT; i++)
{
turtleAnim[i]= new hgeAnimation[TURTLEANIMATIONS]; // Uses default constructor. I don't want that cuz it doesn't exists.
turtleAnim[i]->Play();
}
First, you have to decide whether you want your objects on the stack or the heap. Since they're objects, you probably want them on the heap, which means your 2D array will have 3 stars, and you will access the animations like this:
hgAnimation* theSecondAnimOnTheFourthTurtle = turtleAnim[4][2];
theSecondAnimOnTheFourthTurtle->DoSomething();
If that's what you want, then first you make the matrix of pointers.
hgeAnimation*** turtleAnim = new hgeAnimation**[TURTLECOUNT];
Then you can loop through the turtles. For each turtle, you make an array of pointers to the animations. For each element of that array, you make the animation itself.
for (int turt=0; turt<TURTLECOUNT; ++turt) {
turtleAnim[turt] = new hgeAnimation*[TURTLEANIMATIONS];
for (int ani=0; ani<TURTLEANIMATIONS; ++ani) {
turtleAnim[turt][ani] = new hgeAnimation(parameter1, par2, par3);
}
}
If that looks tricky, deleting all the arrays will be a pain too:
for (int turt=0; turt<TURTLECOUNT; ++turt) {
for (int ani=0; ani<TURTLEANIMATIONS; ++ani) {
delete turtleAnim[turt][ani];
}
delete[] turtleAnim[turt];
}
delete[] turtleAnim;
The fact that this is tricky is a good sign that there's probably a more simple way to design it.
How about a turtle class that has a member like:
class ATurtle {
private:
std::vector<hgeAnimation*> myAnimations;
Then in your class's constructor, you can do whatever you want in order to make the animations.
ATurtle::ATurtle(par1, par2, par3) {
myAnimations.push_back( new hgeAnimation(par1, x, y) );
myAnimations.push_back( new hgeAnimation(par2, z, a) );
myAnimations.push_back( new hgeAnimation(par3, b, c) );
}
That way, you can make your turtles in a single array:
ATurtle* turtles[TURTLECOUNT];
for (int t=0; t<TURTLECOUNT; ++t) {
turtles[t] = new ATurtle(par1, par2);
}
And in your turtle class, you would access the animations like so:
(*(myAnimations.at(1)))->DoSomething();
or
std::vector<hgAnimation*>::iterator i, end=myAnimations.end();
for (i=myAnimations.begin(); i!=end; ++i) {
(*i)->DoSomething();
}
You will still have to call delete on each element of your vector in this case, though, since you called new for every element.
ATurtle::~ATurtle() {
std::vector<hgAnimation*>::iterator i, end=myAnimations.end();
for (i=myAnimations.begin(); i!=end; ++i) {
delete (*i);
}
}