Search code examples
c++stringmacosmemory-corruption

std::string constructor corrupts pointer


I have an Entity class, which contains 3 pointers: m_rigidBody, m_entity, and m_parent. Somewhere in Entity::setModel(std::string model), it's crashing. Apparently, this is caused by bad data in m_entity. The weird thing is that I nulled it in the constructor and haven't touched it since then. I debugged it and put a watchpoint on it, and it comes up that the m_entity member is being changed in the constructor for std::string that's being called while converting a const char* into an std::string for the setModel call. I'm running on a Mac, if that helps (I think I remember some problem with std::string on the Mac). Any ideas about what's going on?

EDIT: Here's the code for GEntity:

GEntity::GEntity(GWorld* world, unsigned long int idNum) {
    GEntity(world, idNum, btTransform::getIdentity());
}

GEntity::GEntity(GWorld* world, unsigned long int idNum, btTransform trans) : m_id(idNum), m_trans(trans), m_world(world) {
    // Init unused properties
    m_rigidBody = NULL;
    m_entity = NULL; // I'm setting it here
    m_parent = NULL;

    // Find internal object name
    std::ostringstream ss;
    ss << "Entity" << idNum << "InWorld" << world;
    m_name = ss.str();

    // Create a scene node
    m_sceneNode = m_world->m_sceneMgr->getRootSceneNode()->createChildSceneNode(m_name+"Node");

    // Initialize the SceneNode's transformation
    m_sceneNode->setPosition(bv3toOv3(m_trans.getOrigin()));
    m_sceneNode->setOrientation(bqToOq(m_trans.getRotation()));
}

void GEntity::setModel(std::string model) {
    m_model = model;

    // Delete entity on model change
    if(m_entity != NULL) { // And by the time this line comes around, it's corrupt
            m_world->m_sceneMgr->destroyEntity(m_entity);
            m_entity = NULL;
    }

    // Create new entity with given model
    m_entity = m_world->m_sceneMgr->createEntity(m_name+"Ent", model);

    // Apply a new rigid body if needed
    if(m_rigidBody != NULL) {
            initPhysics();
    }
}
void GEntity::initPhysics() {
    deinitPhysics();
}

void GEntity::deinitPhysics() {
    if(m_rigidBody != NULL) {
        m_world->m_dynWorld->removeRigidBody(m_rigidBody);
        delete m_rigidBody;
        m_rigidBody = NULL;
    }
}

And here's the definition of GEntity:

class GEntity : public btMotionState {
public:
    GEntity(GWorld* world, unsigned long int idNum);
    GEntity(GWorld* world, unsigned long int idNum, btTransform trans);
    void setModel(std::string modelName);
    void initPhysics();
    void deinitPhysics();
    void getWorldTransform(btTransform& worldTrans) const;
    void setWorldTransform(const btTransform &trans);
    void parent(GEntity* parent);
protected:
    unsigned long int m_id;

    // Physics representation
    btTransform m_trans;
    btRigidBody* m_rigidBody;

    // Graphics representation
    Ogre::SceneNode* m_sceneNode;
    Ogre::Entity* m_entity;

    // Engine representation
    GWorld* m_world;
    GEntity* m_parent;
    std::string m_name;
    std::string m_model; // Used to find physics collision mesh
};

And here's the code calling setModel:

// Setup game world
GWorld* world = new GWorld(win);
GEntity* ent = world->createEntity();
ent->setModel(std::string("Cube.mesh"));

Solution

  • Your problem is that this line is constructing a nameless temporary GEntity inside the constructor body for a different GEntity. The temporary is then thrown away once the statement completes and no further initialization of the non-temporary GEntity is performed.

    GEntity(world, idNum, btTransform::getIdentity());
    

    If you want to share some initialization code between your two constructors you should create a member function that performs the required actions and call this function from both constructors. C++ doesn't (currently) allow you to delegate initialization from one constructor to a different constructor or call two constructors on the same object.