Pushing local objects into a list

有些话、适合烂在心里 提交于 2019-12-23 01:49:13

问题


I have a class

class Invader
{
public:
    Invader();
    ~Invader();
public:
    void Init(InvaderTypes invadertype, CIw2DImage *AlienImage);
    void Update(float dt);
    void Render();
    void SetAlienImage(CIw2DImage *image){ ImageAlien = image; }

    void        setVisible(bool show)       { Visible = show; }
    bool        isVisible() const           { return Visible; }


    Iw2DSceneGraph::CSprite         *AlienSprite;
    Iw2DSceneGraph::CAtlas          *AlienAtals;
    CIw2DImage                      *ImageAlien;
    std::list<Bullet*>              *Bullets;
    CIwFMat2D                       Transform;              // Transform matrix

    bool                             Visible;                // Sprites visible state
    bool                             Canfire;
};


void Invader::Init(InvaderTypes invadertype, CIw2DImage *AlienImage)
{
    if (invadertype == InvaderTypes::TOP_ALIEN)
    {
        //SetAlienImage(AlienImage);
        mImageAlien = AlienImage;
        // Create EnemyTop atlas
        int frame_w = (int)(mImageAlien->GetWidth() / 2);
        int frame_h = (int)(mImageAlien->GetHeight());
        AlienAtals = new CAtlas(frame_w, frame_h, 2, mImageAlien);
        AlienSprite = new CSprite();
        AlienSprite->m_X = 0;
        AlienSprite->m_Y = 0;
        AlienSprite->SetAtlas(AlienAtals);
        AlienSprite->m_W = (float)AlienAtals->GetFrameWidth();
        AlienSprite->m_H = (float)AlienAtals->GetFrameHeight();
        AlienSprite->m_AnchorX = 0.5;
        AlienSprite->SetAnimDuration(2);
    }
    else if (invadertype == InvaderTypes::MIDDLE_ALIEN)
    {

    }
    else if (invadertype == InvaderTypes::LAST_ALIEN)
    {

    }


    Visible = true;
    Bullets = new std::list<Bullet*>();
    Canfire = true;
}



Invader::Invader()
    {

    }
    Invader::Invader(const Invader&other)
    {
        AlienAtals = new CAtlas();
        AlienSprite = new CSprite();
        *AlienAtals = *other.AlienAtals;
        *AlienSprite = *other.AlienSprite;
    }

I try to initialize it by:

list<Invader> *invaders = new list<Invader>();

    int spacing = 10;
for (int i = 0; i < 5; i++)
{
    Invader invader;
    invader.Init(TOP_ALIEN, gameResources->getAlienImageTop());
    invader.AlienSprite->m_X = 50 + spacing;
    invaders->push_back(invader);
    spacing += 50;
}

After pushing the object invader to the list, at the end the invaders list holds pointers that are not initialized. All the pointers got lost the references. I wonder why ?


回答1:


The problem, I assume, is what happens in ~Invader(). Let's simplify the example a ton:

struct A {
    int* p;
    A() { p = new int(42); }
    ~A() { delete p; }
};

A just manages a pointer. When an A goes out of scope, that pointer gets deleted. Now, what happens when we do this:

list<A> objs;
{
    A newA;
    objs.push_back(newA);
    // newA deleted here
}
// objs has one element... but its pointer has been deleted!

The problem is that copying A (which push_back() does) just performs a shallow copy: we copy our pointer. But since A manages its own memory, we need to do a deep copy. That is:

A(const A& rhs)
: p(new int(*(rhs.p)))
{ }

That way, the copied A won't double delete the same pointer. With C++11, this can be much more easily managed with just:

struct A {
    std::shared_ptr<int> p;
    A() { p = std::make_shared<int>(42); }
    ~A() = default; // this line not even necessary
};

Here, copying A will copy the shared_ptr and both copies of A will have a valid object to point to. If you can't use C++11, you can still use boost::shared_ptr<T> for all your memory management needs. And if you can't use that, then you have to write a copy constructor that does a full copy of all your pointer elements.

Or, the simplest solution, would be to just make your container have pointers:

list<A*> objs;
objs.push_back(new A);

Then the "shallow copy" is the right thing to do, and all you need to do is remember to delete everything in the container at the end.




回答2:


Your list contains Invader objects. When you store them in the list, the Invader copy-constructor is called and a copy of the variable invader is stored in the list.

Unless you define a copy-constructor, it will be default simply do a shallow-copy of your object. This may not be what you want. You should write an explicit copy-constructor to make sure that the Invader is copied correctly.

Another solution would be to dynamically allocate Invader objects using the new keyword and storing pointers in the list. If you do that, be careful to make sure that you call delete on the Invader objects in the list when you are done with them.



来源:https://stackoverflow.com/questions/27723974/pushing-local-objects-into-a-list

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!