Archived

This topic is now archived and is closed to further replies.

TookH

DirectX and OOP

Recommended Posts

I have a class that is sort of like this: class Sound { private: LPDIRECTSOUNDBUFFER lpBuf; public: bool Create( char *wavefile ); void Destroy(); Sound() { lpBuf = 0; } ~Sound(); }; bool Sound::Create( char *wavefile ) { // Creates a sound buffer from the file, and // stores the pointer in lpBuf } void Sound::Destroy() { if (lpBuf != 0) lpBuf->Release(), lpBuf = 0; } Sound::~Sound() { Destroy(); } As you can see, the destructor calls Destroy(), which in turn releases lpBuf if it is nonzero. The problem is that if my DirectSound object is released before Destroy() is called, then the sound buffer will have been automatically released but lpBuf will still be nonzero. When the game terminates and the destructor is called, Destroy() tries to release the invalid pointer, which results in an access violation. Besides putting a big comment block that warns you to destroy your Sound objects before you release DirectSound, what can I do to prevent the Destroy() function from releasing the invalid pointer? Is there some way that I can find out if the lpBuf pointer actually represents an existing COM interface? Suggestions are welcome...

Share this post


Link to post
Share on other sites
I must not fully understand your sitution, because I see no problem.
1) When will the thing ever be released???
2) IF it was released it should always be through the call to the destroy() function
3) Also anything that does release it should always set it to null (or in your case zero)
4)THE THING IS IN THE PRIVATE SECTION. thus only that instance of that class can mess with it. so obviously the only time it would be released is by a call into one of the classes functions
which would at the moment only be Destroy() which set's it to null.

IS there still a problem?


Pactuul

"The thing I like about friends in my classes is that they can't access my private members directly."


P.S. maybe if you made a pointer to it that would (I think) be the only other way something outside the class might release it.


Edited by - Pactuul on July 21, 2001 12:15:40 AM

Share this post


Link to post
Share on other sites
When you release a DirectDraw or DirectSound object (with IDirectDraw7::Release() or IDirectSound::Release()), all surfaces and buffers that were created with those objects are automatically released. For example:

// Create a DirectSound object
LPDIRECTSOUND lpDS;
DirectSoundCreate( NULL, &lpDS, NULL );
lpDS->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );

// Describe a buffer to be created
DSBUFFERDESC desc;
// (fill desc with whatever)

// Create a buffer using the DSound object
LPDIRECTSOUNDBUFFER lpBuf;
lpDS->CreateSoundBuffer( &desc, &lpBuf, NULL );

// Release DirectSound -- this automatically releases lpBuf
lpDS->Release();
lpDS = 0;

// BUT, lpBuf is still nonzero! This next block of code
// triggers an Access Violation exception:
if (lpBuf != 0)
{
lpBuf->Release();
lpBuf = 0;
}


Does that clear things up? As I''ve said, this can cause problems with destructors. (And if you don''t believe me, you can check the documentation...)

Share this post


Link to post
Share on other sites
IMHO you should just release the DSoundObject after you release the Buffer. I don't know why'd you'd want to do it any other way.I always remember my high school teacher talking about FILO (First In Last Out). Try releasing the objects in the reverse order in which they were created.

In your case, destroy the instances of class Sound before releaseing the DirectSound interface.

If I were you I'd use pointers to the class instances of Sound instead of direct objects. That way you can control when your app destroys the objects. eg>

Sound *doghowling; // creates a pointer to a sound

DirectSoundCreate( NULL, &lpDS, NULL ); // creates the interface
doghowling = new Sound; // creates the sound in memory
doghowling->Create("xxx.wav"); // load it

..... // whatever

delete doghowling; // calls the destructor
lpDS->Release(); // then releases the DirectSound interface

gl,

infinitycool


Edited by - infinitycool on July 22, 2001 4:13:33 AM

Share this post


Link to post
Share on other sites
I just want to have a level of safety -- I don''t think that it''s good practice to have a class that can''t clean up after itself in some conditions.

Say a programmer creates one of these Sound objects, but forgets to Destroy() it. In this case, the destructor should realize that it was not destroyed properly (ie. lpBuf != 0 but doesn''t point to a valid interface) and not try to release the buffer a second time.

Now, obviously the right thing to do is to call Destroy() properly. But I don''t think that failing to do this should result in a potentially dangerous access violation simply because the destructor was not smart enough to realize that it didn''t need to release the buffer. It''s all about making sure that the class can take care of itself in wierd situations.

So, what I''m asking is, can this be done, or will I simply have to use infinitycool''s suggestion and warn programmers to destroy their Sound objects before releasing DirectSound, else incur an access violation?

Share this post


Link to post
Share on other sites
G''day!

If you want that extra bit of safety there is another approach. Create a SoundManager class. This class will have the DirectSound object as well as a list of all loaded sounds.

To create/load a new sound the programmer would do a SoundManager->Load("mysound.wav"). You can either return a pointer to that sound, or even better, track the sound by name. This would let the developer do things like SoundManager->Play("mysound.wav") and if it wasn''t already loaded, you could then load it and play it.

You could throw in some really cool caching too. But when it''s time to clean up the SoundManager''s destructor would walk down it''s list of sound destroying each one and then clean itself up. The developer wouldn''t have a direct interface to destroy the sounds, it would have to go through SoundManager, say with a SoundManager->Delete("mysound.wav").

Just a thought.


Stay Casual,

Ken
Drunken Hyena

Share this post


Link to post
Share on other sites
Thanks everyone, but I found the solution (I think). It was suggested to me on another board that I should call IDirectSound->AddRef() in my Create() function, and IDirectSound->Release() in my Destroy() function. Theoretically, this should keep DSound alive until all the Sound objects are released. Cool, huh?

Share this post


Link to post
Share on other sites