Search code examples
c++vectorcocos2d-xstdvectorpush-back

A class derived from Cocos2d::Layer has a Pointer Data Member. This member also has vector container. When vector::push_back is called it crashes


I Edited this with my best effort. Hopefully it's now Minimal, Complete, and Verifiable Example

-

  • I am using Cocos2dx V3.6 for our current game
  • The problem I am describing only happens within the custom derived classes from cocos2d::Layer
  • cocos2dx uses static create function which used for mimicking the autorelease functionality of ObjC version. The virtual init() method and the constructor is called within this create function. It is generated by using the CREATE_FUNC macro.
  • The problem is as it has been described in the title. I have a custom derived class from cocos2d::Layer (GameWorldLayer). it has a Pointer Data Member (m_bar). m_bar also has an std:vector property (m_container). I initialise the m_bar inside the overridden virtual init() of the GameWorldLayer. Because of this whenever I try to push something into m_container vector, I get EXP_BAD_ACCESS.

  • if I initialise the Pointer Data Member(m_bar) outside the virtual init() method then everything works fine.

  • In simple terms I just don't understand why initialising the pointer data member outside of the virtual init() method would work but completely fail when it is initialised inside.

I tried to simplify the codes and kept at its bare minimum if you try in your IDE (I am using xcode btw) you should get the same EXP_BAD_ACCESS Im keep getting. (I am sure you already know but you also need cocos2dx 3.6)

GameWorldLayer.h

#ifndef __Flide__GameWorldLayer__
#define __Flide__GameWorldLayer__

#include "cocos2d.h"
#include "bar.h"
class GameWorldLayer : public cocos2d::Layer
{
private:
   Bar * m_bar;
public:

   static cocos2d::Scene* createScene();
   virtual bool init();

   CREATE_FUNC(GameWorldLayer);
   void AddSomethingToBar();
   ~GameWorldLayer();//destructor
 };

GameWorldLayer.cpp

#include "GameWorldLayer.h"

GameWorldLayer::~GameWorldLayer()
{
    delete m_bar;
}

cocos2d::Scene* GameWorldLayer::createScene()
{
   // 'scene' is an autorelease object
   auto scene = cocos2d::Scene::create();

   // 'layer' is an autorelease object
   auto layer = GameWorldLayer::create();

   // add layer as a child to scene
   scene->addChild(layer);

   // return the scene
   return scene;
}

bool GameWorldLayer::init()
{
   //1. super init first
   if ( !Layer::init())
   {
       return false;
   }

   m_bar = new Bar();

   return true;
}

void GameWorldLayer::AddSomethingToBar()
{
   m_bar->PushContainer();
}

Bar.h

#ifndef __Flide__bar__
#define __Flide__bar__
#include <vector>
#include <stdio.h>
using namespace cocos2d;
class Bar
{
private:

   std::vector<int>  m_container;
public:
   Bar(); //constructor
   ~Bar();//destructor

   void PushContainer(); //pushes something into m_container.
};
#endif /* defined(__Flide__bar__) */

Bar.cpp

#include "bar.h"
Bar::Bar()
{
}

Bar::~Bar()
{
   m_container.clear();
}

void Bar::PushContainer()
{
    m_container.push_back(1);//pushing an integer
}

I don't think its necessary but just for completeness. I initialise the GameWorldLayer then call the GameWorldLayer::AddSomethingToBar() inside the AppDelegate::applicationDidFinishLaunching().

bool AppDelegate::applicationDidFinishLaunching()
{
  ...
   // create a scene. it's an autorelease object
   auto scene = GameWorldLayer::createScene();
   ((GameWorldLayer*)scene)->AddSomethingToBar();
  ...
}

Solution

  • My money is on GameWorldLayer::init() which doesn't get called the way you think it does. m_pBar will belong to the layer that you create here: auto layer = GameWorldLayer::create();, and won't belong to your returned Scene object.

    Therefore your Scene object won't access m_pBar, because m_pBar doesn't exist in a cocos2d::Scene. And casting won't do any good either in this case.

    Also, this cast (((GameWorldLayer*)scene)) is bad. You should at least use dynamic_cast