Jump to content
  • Advertisement
Sign in to follow this  
Chris_J_H

Shared pointers in event driven game engine

This topic is 2389 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I am coding a simple event driven game engine in C++ (mostly for self-educational purposes). I have been using boost::shared_ptrs fairly liberally, taking the view that this will ease my burden considerably on object life managment (still taking care to avoid cyclic dependancies)....
However, I encounter the following difficulty: I define a Game Actor class that inherits 2 interfaces via multiple inheritance: IActor & IEventListener. My Event Management System keeps boost::shared_ptrs to IEventListeners and my Game Logic System keeps boost::shared_ptrs to IActors. On game reset I clear both systems and find that as my 2 systems are reference counting the same object/parts of the same object independently I get an error on attempt to double delete heap memory. I guess my question is more about design than a language issue (I think I have defined the problem correctly...). Where do I go from here? Use only weak/naked pointers in the Event System freeing it from life-management responsibilities...? (which I guess makes sense, its only an inter-object messaging system after all....) I suppose, naively, I thought introducing shared_ptrs would make life much easier but I am becoming disabused of that.. any guidance/comments appreciated!

Share this post


Link to post
Share on other sites
Advertisement
As you can see, using shared_ptr is not a silver bullet. It's still a good idea to know who owns the object (i.e., who is responsible for cleaning it up).

You can make your life simpler by not using multiple inheritance. You don't need to make every object an IEventListener to make them listen to events. All you need to do is register callbacks using boost::function, and now your objects don't need to inherit from anything.

Also, there is something broken if you get double deletes using shared_ptr. My guess is that you first create a naked pointer to the object and then create two shared_ptrs from it. This is wrong. You should create a single shared_ptr from your raw pointer and use it everywhere.

I hope that helps.

Share this post


Link to post
Share on other sites

As you can see, using shared_ptr is not a silver bullet. It's still a good idea to know who owns the object (i.e., who is responsible for cleaning it up).

You can make your life simpler by not using multiple inheritance. You don't need to make every object an IEventListener to make them listen to events. All you need to do is register callbacks using boost::function, and now your objects don't need to inherit from anything.

Also, there is something broken if you get double deletes using shared_ptr. My guess is that you first create a naked pointer to the object and then create two shared_ptrs from it. This is wrong. You should create a single shared_ptr from your raw pointer and use it everywhere.

I hope that helps.


Thanks for your comments, Alvaro. I like the rigour of the interface so I think I'll stick with it if I can. I am not very familiar with passing around function objects so they look messy to me... and I'll probably end up needing to use boost::bind &/or boost::lambda as things get more complicated - but clearly something I need to become more familiar with.
In my Game Actor class constructor I pass a boost::shared_ptr<IEventListener*>(this) to the Event Manager so I guess in essence I do what you say (using a raw pointer twice)-- however, if I somehow cast the IActor shared pointer into a IEventListener shared pointer would that help? - I think not - right? - In essence I would still be following 2 ref counting regimes to the same memory... Thinking further: the boost::function method effectively means I would be passing a raw function pointer to the Event Manager for the call back and if I am happy to do that then why not just pass an IEventListener* to the Event Manager and avoid the object management conflict of the shared_ptrs. So given where I am - that's probably simplest...

Share this post


Link to post
Share on other sites

In my Game Actor class constructor I pass a boost::shared_ptr<IEventListener*>(this) to the Event Manager so I guess in essence I do what you say (using a raw pointer twice)


You might look into enable_shared_from_this. It might help provide some of the functionality you are looking for. The multiple inheritance might cause some problems still.

Share this post


Link to post
Share on other sites
Perhaps this will shed some light:
#include <iostream>
#include <boost/shared_ptr.hpp>

struct Base1 {
virtual ~Base1() {}
};

struct Base2 {
virtual ~Base2() {}
};

struct Derived : Base1, Base2 {
~Derived() {
std::cout << "Derived destroyed!\n";
}
};

int main() {
// Derived *d = new Derived; <-- This would result in a double destruction! Use the following line instead.
boost::shared_ptr<Derived> d(new Derived);

boost::shared_ptr<Base1> b1(d);
boost::shared_ptr<Base2> b2(d);
}

Share this post


Link to post
Share on other sites

Perhaps this will shed some light:
#include <iostream>
#include <boost/shared_ptr.hpp>

struct Base1 {
virtual ~Base1() {}
};

struct Base2 {
virtual ~Base2() {}
};

struct Derived : Base1, Base2 {
~Derived() {
std::cout << "Derived destroyed!\n";
}
};

int main() {
// Derived *d = new Derived; <-- This would result in a double destruction! Use the following line instead.
boost::shared_ptr<Derived> d(new Derived);

boost::shared_ptr<Base1> b1(d);
boost::shared_ptr<Base2> b2(d);
}



Great! I am definitely learning here.... clearly the shared_ptr is a more complex beast than I thought. This code avoids the double destruct and the various shared_ptrs within the class hierarchy seem to keep track... eg. if I reset b1, then b2 also shows a ref decrement. (magic! - I suppose ultimately in the depths the derived shared pointers ref-count the base object...). And to enable all this, as you say, I need to make sure I seed new shared_pts from old shared_ptrs (not raw/this). Thanks.

Share this post


Link to post
Share on other sites

[quote name='Chris_J_H' timestamp='1320676016' post='4881404']
In my Game Actor class constructor I pass a boost::shared_ptr<IEventListener*>(this) to the Event Manager so I guess in essence I do what you say (using a raw pointer twice)


You might look into enable_shared_from_this. It might help provide some of the functionality you are looking for. The multiple inheritance might cause some problems still.
[/quote]

Thanks for the tip - yes, absolutely, I need this if I am going to use this to generate a shared_ptr without messing up the ref count. Also, I note on investigation that I mustn't use this within the constructor as, for this to work, there must be a pre-existing shared_ptr to the resource.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!