C++ Struct Destructor Question

Started by
8 comments, last by Zahlman 16 years, 5 months ago
Is memory allocated for struct on the heap? If I have DirectX interfaces needed to release, can I do it in the destructor of a structure? I have some wierd problems with a struct destructor like this: struct EnemyShip { IDirect3DTexture9* tex; EnemyShip(); ~EnemyShip(); }; EnemyShip::EnemyShip() { HR(D3DXCreateTextureFromFile(gd3dDevice, TEXT("alienship.bmp"), &(tex))); } EnemyShip::~EnemyShip() { ReleaseCOM(this->tex); }
Advertisement
Quote:Original post by ynkm169
Is memory allocated for struct on the heap?


Depends on the instance. Did you create it with new?
Quote:Original post by Driv3MeFar
Quote:Original post by ynkm169
Is memory allocated for struct on the heap?


Depends on the instance. Did you create it with new?


I use the following line to initialize IDirect3DTexture9* tex, so I did not use NEW. However I think I am supposed to release it with releaseCOM(); I put it in the structure destructor and my program says access violation.


HR(D3DXCreateTextureFromFile(gd3dDevice, TEXT("alienship.bmp"), &(tex)));
whatsup with this releaseCOM thing?

where did you see that as an example?

isn't there a direct3d9texture->release() or something like that?
Quote:Original post by Maverick_24
whatsup with this releaseCOM thing?

where did you see that as an example?

isn't there a direct3d9texture->release() or something like that?


sorry. It is defined as this:

#define ReleaseCOM(x) { if(x){ x->Release();x = 0; } }

I just found the problem. I have the following function to initialize my list of structures. It turns out that even if I pushed the structure down the std::list, the destructor is still called for it at the end of the function. Therefore, later on, when I say something like this:

std::list<EnemyShip>::iterator i = mEnemyShipList.begin();
HR(mSprite->Draw(i->tex, 0, &mShipCenter, 0, D3DCOLOR_XRGB(255, 255, 255)));

ERROR is produced when I use "i->tex" that has already been released.


void SpriteDemo::enemyShipList()
{
EnemyShip enemyShip1;
enemyShip1.pos = D3DXVECTOR3(-300.0f, -300.0f, 0.0f);
enemyShip1.realPos = D3DXVECTOR3(-300.0f, -300.0f, 0.0f);
enemyShip1.bulletColorNormal = D3DCOLOR_XRGB(255, 255, 255);
mEnemyShipList.push_back(enemyShip1);

EnemyShip enemyShip2;
enemyShip2.pos = D3DXVECTOR3(300.0f, -300.0f, 0.0f);
enemyShip2.realPos = D3DXVECTOR3(300.0f, -300.0f, 0.0f);
enemyShip2.bulletColorNormal = D3DCOLOR_XRGB(255, 0, 255);
mEnemyShipList.push_back(enemyShip2);

EnemyShip enemyShip3;
enemyShip3.pos = D3DXVECTOR3(300.0f, 300.0f, 0.0f);
enemyShip3.realPos = D3DXVECTOR3(300.0f, 300.0f, 0.0f);
enemyShip3.bulletColorNormal = D3DCOLOR_XRGB(255, 255, 0);
mEnemyShipList.push_back(enemyShip3);

EnemyShip enemyShip4;
enemyShip4.pos = D3DXVECTOR3(-300.0f, 300.0f, 0.0f);
enemyShip4.realPos = D3DXVECTOR3(-300.0f, 300.0f, 0.0f);
enemyShip4.bulletColorNormal = D3DCOLOR_XRGB(0, 255, 255);
mEnemyShipList.push_back(enemyShip4);
}
When you add an object to a list, you create a copy. Naturally you now have two objects. Each will have its destructor called in due course. For the enemyShip1/2/3/4 objects, "due course" is at the end of scope for the enemyShipList() member function.

At this point, ReleaseCOM is called. But the copy of each object sitting in the list contain tex pointers that point to the objects you just released. Obviously this is bad, because these objects will now operate on "dangling" pointers. I think you understand this, already, but it's worth being crystal clear.

So, things to lookup/read in order to fix this:

1. The rule of three
2. How to make an object un-copyable by declaring private copy constructor and assignment operators (or how to use boost::noncopyable)
3. reference-counted smart pointers (e.g. boost::shared_ptr)

2 and 3 are often used in combination.
Keep in mind the rule of three. Since you have a non-trivial destructor, you'll want your own copy constructor and assignment operator.

Edit: Slow
Thanks a bunch!
COM provides a smart-pointer IIRC which is useful for this sort of thing.

Also, it is not useful to set a pointer to 0 in a destructor, and anyway, a macro like that can be done as a function with no real disadvantages and the very definite advantage of having an actual function instead of a text substitution hack. Such "release" macros (including the classic version using 'delete' instead of an API Release() call) are a dangerous relic of a forgotten era.

Also, use constructors to simplify your code.

 EnemyShip enemyShip1;enemyShip1.pos = D3DXVECTOR3(-300.0f, -300.0f, 0.0f);enemyShip1.realPos = D3DXVECTOR3(-300.0f, -300.0f, 0.0f);enemyShip1.bulletColorNormal = D3DCOLOR_XRGB(255, 255, 255);mEnemyShipList.push_back(enemyShip1);


Notice how you can just write 'D3DXVECTOR3(-300.0f, -300.0f, 0.0f)' instead of making a separate D3DXVECTOR3 variable, assigning to its members, and then proceeding with that? You should set up your code to offer similar functionality:

EnemyShip::EnemyShip(D3DXVECTOR3 position, D3DCOLOR_XRGB bulletColor) :pos(position), realPos(position), bulletColorNormal(bulletColor) {  // as before}mEnemyShipList.push_back(EnemyShip(D3DXVECTOR3(-300.0f, -300.0f, 0.0f),                                   D3DCOLOR_XRGB(255, 255, 255));

This topic is closed to new replies.

Advertisement