How to implement a c++ class destructor?

Hi there,
I need some help with a c++ memory allocation problem (I think…)…

So I have a game that runs a few class instances, and then when the game is over I want to reset it, so destroy all the instances (except the main controller that calls the destructors), re-instanciate my fresh objects and start the game again. But I think I don’t know how to implement a destructor properly because what I did doesn’t work well :

I can refresh the game once, but at the second time I have an hard fault error, so I think there is a memory overflow somewhere. But the output of the RAM doesn’t move at all, I have 12KB of RAM available all the time, it doesn’t move when I refresh the game, and the second time, when it crashes, the crash happens immediately on the button press so I can’t see the output…

Anyway I’m kind of lost. Here the chunk of code that handle game reset:

void GameController::resetGame()
{
    
    delete this->character;
    delete this->space;
    delete this->view;
    delete this->cinematic;
    delete this->backpack;
    delete this->objCol;
    delete this->stkCtnr;

    this->character = new Character();
    this->space = new Space();
    this->view = new View();
    this->cinematic = new Cinematic();
    this->backpack = new Backpack();
    this->objCol = new ObjectCollection();
    this->stkCtnr = new StackContainer();

    this->initGame();
}

I also tried it that way, with exactly the same result:

void GameController::resetGame()
{
    this->character->~Character();
    this->space->~Space();
    this->view->~View();
    this->cinematic->~Cinematic();
    this->backpack->~Backpack();
    this->objCol->~ObjectCollection();
    this->stkCtnr->~StackContainer();

    this->character = new Character();
    this->space = new Space();
    this->view = new View();
    this->cinematic = new Cinematic();
    this->backpack = new Backpack();
    this->objCol = new ObjectCollection();
    this->stkCtnr = new StackContainer();

    this->initGame();
}

And the destructors ar just declared this way for all classes :

class Cinematic
{
  private:
    uint8_t fadeFrame;
    bool fadeNextFrame;
    //...
  public:
    Cinematic();
    ~Cinematic() {};
    bool playFadeIn();
    bool playFadeOut();
   //...
};

I didn’t specify any particular behavior, since I saw in most examples that there is always nothing more that a text output like that

~Test() { cout << "Destructor is executed\n";  } 

Anyway I can’t find what I’m doing wrong, can it be because I use pointers for my instances ?

If needed my code is on github here

Thank you if someone can give me a tip…

Well I figured out another simpler solution for my problem, I finally did’nt need to destroy the objects, I simply implemented a reset method for each of them… works fine now.

But I’m still interested in how to use a destructor properly for any other usecase, so feel free :slight_smile: !

Hi,
I did that for a little game engine I made. It’s an un-finished game that I use during workshop, where the students have to fill the holes, and new features, design the level etc.

You have one base “object” class that the other classes inherit from : “enemies”, “bullet”, “particle”, “player”. The mail “engine” class (singleton) has an array of “object” that contains all the game object and call their “update”, “draw”, etc. methods. The “engine” can also add new objects, or destroy them.

The problem with that design is that if the objects list is full, you can’t spawn anything. So if the screen is full of particles, you can’t spawn an enemy. Also it’s a bit complicated to make the different objects to interact together in different ways, so if I had to re-do it I would use separate arrays for each kind of object.

The advantage of your design, where you have a fixed list of objects that you enable/disable and reset, instead of instantiating/deleting, is that you won’t have memory fragmentation problem, which could become an issue with dynamic allocation as we only have 32KB or RAM and no proper OS.

Instanciate new object and add it to the engine
Engine::addObject(new Player());

Add objects to the list

int Engine::addObject(Object * object) {
  for (int i = 0; i < ENGINE_NUM_OBJECTS; i++) {
    if (objects[i] == 0) {
      objects[i] = object;
      return i; //object created
    }
  }
  delete object;
  return -1; //no more space
}

Delete objects

  //unallocate dead objects
  for (int i = 0; i < ENGINE_NUM_OBJECTS; i++) {
    if (objects[i] == 0) continue;
    if (objects[i]->life <= 0) {
      objects[i]->die();
      delete objects[i];
      objects[i] = NULL;
    }
  }

The original physics / game / platformer game engine:

The simplified version, turned into a shoot them up where you have to grow trees:

2 Likes

Indeed I didn’t find out any accessible solution for me to have some dynamic collection of objects, so I chose to have fixed arrays, filled with 0 when empty. But the way you do it looks pretty cool and understandable, I didn’t think of that ;)!

Aurélien’s solution is the right solution for the META, due to the fragmentation of memory.

However, in the case of your project, you don’t necessarily need to do it this way, since all your collections are already statically allocated.

I would have to clone your repository and go back to the commit that integrated your original resetGame() method to try to reproduce the problem you encountered.

On the other hand, yesterday, I simply downloaded a ZIP of your code (you had already applied your commit…), I found the capsules of the battery, but I didn’t understand at all what I should do with them… I visited a lot of “rooms”, but… ???

I also didn’t find anything that could lead me to the lose screen.

So, I wanted to put the following instructions directly in the code to immediately find myself on the lose screen by pressing the MENU button:

void GameController::updateGame()
{
  if (this->gameWon) this->handleWin();
  else if(this->gameLost) this->handleLose();
  // ----------------
  // my piece of code
  else if (gb.buttons.pressed(BUTTON_MENU)) {
    this->gameLost = true;
  }
  // ----------------
  else
  {
      this->getInputs();
      this->character->update(this->space);
  }
}

Does the bug occur in this way? Otherwise, what do you have to do to lose?

I’ll try to take a look tonight when I get home to see if I can identify anything…

1 Like

Hi Steph, thanks for trying all this !
You can checkout this commit to see to code at the moment I made this post https://github.com/Codnpix/Glitch_META/blob/b069b37f7dc0db9edcf15f6fd10945093f586eae/GameController.cpp
I think it’s normal you didn’t know what to do with the objects, in that state the game isn’t very explicit, when I will put it online (soon) there will be some explanations about what to do.
Anyway, to lose or win the game, you have to collect the capsules (picked with the B button, you also can drop them with the same button), then you have to go in the room that looks like a kind of lab. And you’ll find a vertical container. When you are facing that container you can drop the capsules and they will take place into the container. If you replace the 4 capsules in the container in the right order you win, else you lose.

I thought we should do something in this lab!!!.. but I couldn’t find out where :joy:

Does the bug still occur if I cause the connection to the lose screen by pressing the MENU button?

I didn’t try yet, I tell you this as soon as I can ;).

Ok I tried with your trick with the menu button, and indeed the bug doesn’t occur in that way, I can do it how many time I want…

@Steph Oups sorry I talked to fast, the reset wasn’t called, I messed up with my Git checkout (Arduino IDE doesn’t have a file watcher so when I checkout with the editor open it messes up the code…)
Anyway THE BUG DOES OCCUR exactly the same way with the menu button…
I pushed the checkout on a new branch “reset_bug_tests” I you want to try…

Okay, I’ll watch it tonight…

… but forget the Arduino IDE! Instead, switch to Visual Studio Code :wink:

1 Like

Most of the time I code on Atom, but I have the IDE open when I want to do compile and upload the code… I didn’t try yet to set up another solution for doing this… But yeah I think I will drop Arduino IDE soon or later ! It’s really not great…

It seems that the problem comes from the official lib, and especially from the Image class.

Thank you @minirop for enlightening our lanterns on this subject!

The copy constructor seems to induce a malfunction when destroying the instance of an Image.

In other words, to avoid finding yourself in this case, you must modify the View class by declaring the spriteSheet attribute as a pointer! Therefore:

in View.h file:

Image* spriteSheet

in View.cpp file: (scroll down to see all the code to replace)

void View::setSpriteSheet(Character * character)
{
  if (character->getAnimationState() == "WALK" )
  {
    this->spriteSheet = &charSpriteWalk;
  } else if (character->getAnimationState() == "JUMP"
  || character->getAnimationState() == "FALL")
  {
    this->spriteSheet = &charSpriteJump;
  } else if (character->getAnimationState() == "CLIMB")
  {
    this->spriteSheet = &charSpriteClimb;
  } else if (character->getAnimationState() == "STAND")
  {
    this->spriteSheet = &charSpriteBase;
  } else if (character->getAnimationState() == "ENTER")
  {
      this->spriteSheet = &charSpriteEnter;
  } else if (character->getAnimationState() == "TAKE")
  {
      this->spriteSheet = &charSpriteTake;
  }
}

void View::drawCharacter(float X, float Y, Character * character, int8_t charW, uint8_t charH)
{
  gb.display.setPalette(CHAR_SPRITE_PALETTE);
  this->spriteSheet->setFrame(this->charAnimFrame);
  gb.display.drawImage(X, Y, *this->spriteSheet, charW, charH);
}
1 Like

Thanks to both of you for the investigation :slight_smile: !