-1

In my code I have a SoundManager class, which contains and operates on all the sounds for my game. This class needs to be instantiated and it's methods called by multiple other classes. However I wish for there only to be one set of sounds occupying memory, so in the interest of efficiency all of the assets are declared as static shared_ptrs.

#include "SoundManager.h"

static shared_ptr<ISoundEngine> sEngine;

static shared_ptr<ISoundSource> hoverSound;
static shared_ptr<ISoundSource> confirmSound;
static shared_ptr<ISoundSource> mainBGM;
static shared_ptr<ISound> bgmInterface;

SoundManager::SoundManager(void)
{


//first we need to create the irrKlang sound engine instance
    if(!sEngine)
    {
        sEngine.reset(createIrrKlangDevice());
    }


    if(!hoverSound)hoverSound.reset(sEngine->addSoundSourceFromFile("Sounds/ButtonHover.mp3"));
    if(!confirmSound)confirmSound.reset(sEngine->addSoundSourceFromFile("Sounds/ButtonConfirm.mp3"));
    if(!mainBGM)mainBGM.reset(sEngine->addSoundSourceFromFile("Sounds/mainBGM.mp3"));


    //set some default volumes
    hoverSound->setDefaultVolume(1.0f);
    confirmSound->setDefaultVolume(0.4f);
    mainBGM->setDefaultVolume(0.5f);


}


SoundManager::~SoundManager(void)
{   
}

This SoundManager is instantiated in my main() function and every time i need to load the title-screen (SoundManager is instantiated in this titlescreen class too). Initialising and destroying the title-screen over and over do not cause a problem. The static shared_ptrs objects are not destroyed as they are still in use by the main functions instance of SoundManager.

Now this all works fine in practice for running my game. However when it comes to exiting cleanly, when the static objects above are dismantled, unhandled runtime exceptions (access violations) are thrown at me. With VS2012's debugger pointing me to a line in memory.h.

private:
    virtual void _Destroy()
        {   // destroy managed resource
        delete _Ptr;       <<<<<<<<<The debugger points to this line
        }

I was to understand that similar to obj-c, c++ shared_ptrs use reference counters to ensure that the objects aren't deleted until no object exists that require their use any more. I don't understand what could be causing these errors.

Maybe an important part I shouldn't omit: My game is exited via a call to exit(0); as close to the main() function as possible. I haven't taken any sort of action to cleanup the SoundManagers members before doing this, as I thought the shared_ptr's handled this.

Does anybody know what could be causing my cleaning up problem?

Guy Joel McLean
  • 1,019
  • 4
  • 12
  • 34
  • 2
    what type of shared_ptr are they? Calling `reset()` on a `std::shared_ptr` or `boost::shared_ptr` will clear that `shared_ptr`, if it is the last one referencing the object then the object will be deleted, if not the object is still shared. – mark Apr 17 '13 at 14:58
  • Are you sure the poitners are not reused? If the `sEngine->addSoundSourceFromFile` returns the same pointer twice, it gets deleted twice, because you assign it to two different `shared_ptr`s, that don't know about each other. Or if the `sEngine` calls delete on one of the pointers, the `shared_ptr` will have a hard time cleaning up what is not there any more. Write down the value of the pointer that causes the access violation and try to break at any delete that tries to free that exact address. – Arne Mertz Apr 17 '13 at 15:00
  • `boost::shared_ptr` has member functions that allow you to determine if the `shared_ptr` is unique or get its reference count. – mark Apr 17 '13 at 15:01
  • @mark I thought there was only one type of shared_ptr? (I originally did this with unique ptrs' instead by the way, but it yielded the same problem). – Guy Joel McLean Apr 17 '13 at 15:08
  • @ArneMertz `sEngine->addSoundSourceFromFile()` returns a pointer to a new object of type `ISoundSource`. I decided to make the pointers shared_ptrs because they're static and I only wanted there to be one instance of the object they point to thoughout the whole program, whilst still having multiple instances of the `SoundManager` class. Is it possible for the `sEngine` or any object for that matter, to take ownership of a pointer initially managed by a `shared_ptr` and delete it without the original owning shared_ptr having something to say about that? Unless I call reset() in a stupid place? – Guy Joel McLean Apr 17 '13 at 15:19
  • @ArneMertz I will also try to track the memory address like you suggested. – Guy Joel McLean Apr 17 '13 at 15:23
  • You can take ownership of a pointer managed by a `shared_ptr` only by calling `reset` or `release` on that `shared_pointer` or assigning a new value to it. Otherwise the `shared_ptr` will continue to assume that it has ownership and delete it, regardless of what you did to it meanwhile. – Arne Mertz Apr 17 '13 at 15:23
  • C++11 introduced `std::shared_ptr` which is heavily based on `boost::shared_ptr`. – mark Apr 17 '13 at 15:25

2 Answers2

2

If you want to manually free a resource used by a shared_ptr you need to call reset. As for using static shared_ptr I don't think I get the reasoning. The whole point is that they do not copy of the resource around, so you will only have one resource.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • I decided to make the pointers shared_ptrs because they're static and I only wanted there to be one instance of the object they point to though out the whole program, whilst still having multiple instances of the SoundManager class. However making them 'shared_ptr's alone does not do this, which is why I made them static. Does this explain my reasoning? – Guy Joel McLean Apr 17 '13 at 15:22
1

Your are using the IRRKLang library. This library comes as a precompiled binary (dll if you are on windows). This library makes itself binary compatible by using pure virtual bases. This works, but you cannot delete an object for a library like that, because your program new/delete are different from the new/delete of the library. Those type of libraries provide a way to deallocate the memory, this this case drop.

To use shared_ptr etc you have to use a custom deleter. Take a look at Using custom deleter with std::shared_ptr to see how to do it and modify it to your own needs.

In your case since you are using Visual Studio 2012 you can probably do something like this

template<class T>
struct IrrDeleter{
   void operator()(T* t){
       t->drop();
   }
};

then change all your reset lines to include the deleter, for example

sEngine.reset(createIrrKlangDevice(),IrrDeleter<ISoundEngine>());
Community
  • 1
  • 1
John Bandela
  • 2,416
  • 12
  • 19
  • Thank you, I think this is what I needed. So are you saying that the shared_ptrs way of deleting the data it holds conflicts with IRRKlang's ISoundEngine's way of managing it's own memory when it goes out of scope? – Guy Joel McLean Apr 17 '13 at 15:44
  • In a way. Your delete (even if manual outside of shared_ptr) won't work for ISoundEngine. That is because your delete and IrrKlang delete are different because IrrKlang was probably compiled with a different compiler/standard library. All the Irrklang classes derived from IRefCounted which provides the drop method that will delete it correctly – John Bandela Apr 17 '13 at 16:00