[Solved] Access violation reading location when referencing object extend Node

Update 1: Bottom of this post
Update 2: A reply of this post
Update 3: Post#8

Hi everyone, I am getting this error at run time.

I will try to simplify this by showing only relevant code.

I am extending my People class to cocos2d::Node so I can attempt to have multiple objects update themselves. In other words, call scheduleUpdate in every object. Since each node can only have 1 update function, this is my attempt to do it. Is this ok? (Instead, I can have update of layer call EVERY object’s update function, but I want to release this dependency and let objects handle themselves.)

People.h constructor
People(int h, std::string n, const std::string& filename, Layer* layer);

People.cpp constructor

#include "People.h"

//base class People

People::People(int h, std::string n, const std::string& filename, cocos2d::Layer* layer){    
       
    sprite = cocos2d::Sprite::create(filename);       

    this->addChild(sprite,0);
    layer->addChild(this,5); //'this' is causing the error    **UPDATE BELOW**  
}

bool People::init(){
    CCLOG("ppl init");    
    return true;
}    

void People::update(float dt){
    CCLOG("PEOPLE reached");
}

Call
People d(20, ā€œPeopleā€, ā€œa.pngā€,this); //ā€˜this’ is a CCLayer

Can someone point me in the right direction?

Update 1: It turns out that this line gets executed fine, in fact the objects initialization is perfectly fine, but on the program gives an error at CCNODE.cpp ,

// MARK: events

void Node::onEnter()
{
    if (_onEnterCallback)
        _onEnterCallback();

#if CC_ENABLE_SCRIPT_BINDING
    if (_scriptType == kScriptTypeJavascript)
    {
        if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnEnter))
            return;
    }
#endif
    
    _isTransitionFinished = false;
    
    for( const auto &child: _children)
        child->onEnter();  //causing error here.
    
    this->resume();
    
    _running = true;
    
#if CC_ENABLE_SCRIPT_BINDING
    if (_scriptType == kScriptTypeLua)
    {
        ScriptEngineManager::sendNodeEventToLua(this, kNodeOnEnter);
    }
#endif
}

I tried to debug it up to this point, but I am unable to do so at the moment. Does anyone know what is going on?

Through debugging, I was able to find the ā€˜child’ that is causing this problem, but I cannot make much sense of it.

If you are unaware ā€œaccess violationā€ means you are trying to access an invalid piece of memory. I think it can probably happen if the memory was freed or never initialised, and possibly if the memory is a null pointer. The child in your second picture does look erroneous.

I really want to help, but I cannot produce an error using the code you have provided.

I managed to produce an error in the exact same location as yours when I released my ā€œPeopleā€ object enough times to have the freed from memory. Any chance you are releasing your People objects at some point, either through peopleObj->release() or removing it from its parent at some point and then readding it?

In debug builds, memory is sometimes initialized to 0xCCCCCCCCC. The value of your pointer is 0xCCCCCE44. Also, you are adding things in a constructor. Putting that together, I think that most likely what is occurring is that you are using some pointer that has not been initialized yet. Rather than do work inside of the constructor (other than initialization), try doing that work in the init method instead. Also, check your constructors and make sure you have initialized all pointers before you use them.

Thanks for replying. I am trying to trace back to where the object might be released, but I am unable to find it. I have extracted the minimal code to try to debug, which create a similar error ( this is using the cocos2d fresh project),

People.h

#ifndef _People_H_
#define _People_H_

#include "cocos2d.h"
using namespace cocos2d;

class People : public Node {
private:
    int health;
    std::string name;
    Vec2 location;
    Sprite* sprite;

public: 
    People();

    People(int h, std::string n, const std::string& filename, Layer* layer);    
    
    void set_location(Vec2 a);        

    virtual bool init();

    virtual void update(float dt);
};
#endif

People.cpp

#include "People.h"

//base class People

People::People(){

}

People::People(int h, std::string n, const std::string& filename, cocos2d::Layer* layer){    
    
    
    health = h;
    name = n;

    sprite = cocos2d::Sprite::create(filename);
    sprite->setPosition(location);

    this->addChild(sprite,0);
    layer->addChild(this,5);
    //layer->addChild(sprite, 5);
}

bool People::init(){
    CCLOG("ppl init");    
    return true;
}

void People::set_location(cocos2d::Vec2 a){
    location = a;
    sprite->setPosition(location);
}

void People::update(float dt){
    CCLOG("PEOPLE reached");
}

HelloWorldScene.cpp (I marked the only change from original cpp, which is at the bottom of init())

#include "HelloWorldScene.h"
#include "People.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{    
    int a = 0;

    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

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

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{    

    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
    closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label
    
    auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);
    
    // position the label on the center of the screen
    label->setPosition(Vec2(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - label->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(label, 1);

    // add "HelloWorld" splash screen"
    auto sprite = Sprite::create("HelloWorld.png");

    // position the sprite on the center of the screen
    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer
    this->addChild(sprite, 0);

    People d(20, "People", "Officer.png", this);   //The only change      
    list[0] = &d;

    
    return true;
}


void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
    MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
    return;
#endif

    Director::getInstance()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
}

HelloWorldScene.h

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include "People.h"

class HelloWorld : public cocos2d::Layer
{
public:

    People* list[1];

    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static cocos2d::Scene* createScene();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
};

#endif // __HELLOWORLD_SCENE_H__

Thanks for reading, I put code in the init() of the People class, but it doesn’t get executed before the error occurs.

With my implementation, I need to ref the layer to add the People object. Unless I make an instance variable, the only way to ref the layer object is through the constructor right?

I apologize if this is a easy topic, or if I am using incorrect terminology. I’ve only dealt with C for 6 months, while spending a lot of time with Java.

@grimfate @mannewalis , I think I solved the problem by declaring People object this way:

auto d = new People (20, "People", "Officer.png",this);
list[0]=d;

Is it correct to assume that if I declare an object this way: People d (20,ā€œPeopleā€ā€¦) then it will get recycle at the end of the function, that’s why it’s causing the initial issue?

Additionally, the init() function in the People class never executes automatically. Am I doing something wrong? My goal is to scheduleupdate() insinde the init() function.

I wonder if the problem was that d was supposed to be a pointer, i.e. People* d(20, "People", "Officer.png", this); with the asterisk added to People. Of course if auto works then that’s good too.

I don’t think it would be recycled, because I think it’s only autoreleasing items that do, e.g. items created using their create method.

init is not called automatically. The create method that most Cocos classes use is what calls the init method for objects automatically, so you may want to implement that.

Thank you for the insights. Then I won’t be using init(), Putting scheduleUpdate() in People’s constructor seems to work. If I use People *d, it gives me an error saying int( refering to the first param) cannot be initialized to an entity of People * .

@grimfate one last thing, do I need to provide a destructor for people’s class? I have a little hard time grasping the destructor concept, I’ve already read the five rule.

What about People* d = new People (20, "People", "Officer.png",this);?

Whenever you allocate memory using something like malloc or new, that memory stays in use until either you free it or the app ends. You have limited memory on a computer/device, so you need to deallocate memory when you have finished with it so that it can be used for something else that will need memory. The destructor is used to tell your object that it is about to be removed from memory. This allows your object to deallocate any memory it has allocated without fear it will be used again.

For example, imagine you have a class Person and imagine your Person class has an object Head which represents your characters head. You might allocate the memory for the person’s head in your constructor, e.g.

Person::Person() {
  head = new Head();
}

You have now allocated memory for head and it will continue to use memory until you free it. Your person will always have a head, so you do not want to deallocate the memory while the person is still in use. But when the person is not in use and you delete your person object, you need to free the memory used by head. ~Person tells you that your object is being freed so you no longer need head.

Person::~Person() {
  // person is being removed, so head is no longer used
  delete head;
}

I believe that you don’t need to implement a destructor if you don’t want to. So if you have no memory you need to manually free, then you probably don’t need to bother. (You may find other uses for the destructor other than memory management. It is effectively just the place where you know your object is being permanently removed.)

1 Like

@grimfate Thank you for your explanation. People* d = new People (20, ā€œPeopleā€, ā€œOfficer.pngā€,this); works as well.

Were you using People::create to create a new People instance? If so you need to implement a CREATE_FUNC in the People class, otherwise it will create a Node instead, and will probably crash when you try to use it.

I am not using create(), it seems to be ok right now. I will keep that in mind if I run in to any problems with it in the future. In java, there are interface classes, if we extend an interface, we must implement ALL functions from the interface. Is there such a requirement in C++? In other words, are there functions that we MUST implement in subclasses? Are they marked with any keywords?

Yeah there are pure virtual interfaces.

class MyInterface
{
public:

    virtual void must_implement_me() = 0;
};

If you derive from this interface, you must implement the method.

1 Like