0

I have some small classes including references to external ressources. As they are small but heavily passed around and often stack allocated, I do not pass pointers but the instances itself. So usually the copy-constructor, and sometimes assignment is used.

This however results in unbalanced ressource management, where external ressources gets freed as soon the first instance leaves some scope, while there are still copies around.

For pointers, there are smart but often pricy things like std::shared_ptr.

However, how to handle the instance-passing situation? It seems it's almost impossible to know by one instance if it is the last of its kind getting destructed?

dronus
  • 10,774
  • 8
  • 54
  • 80
  • Have you looked at how `unique_ptr` works? – Alan Stokes Dec 31 '13 at 21:37
  • `unique_ptr` seems to be meant to forbid several objects sharing it? So this fits not my pattern, I think `shared_ptr` is closer to my needs.. I first thought of an `shared_ptr` of my object on itself, but that would prevent the first instance to vanish before copies do. – dronus Dec 31 '13 at 21:48
  • 1
    I second @AlanStokes's motion. A reminder that not all pointers are owning: you can use `.get()` on a `std::unique_ptr` and then pass that 'normal' pointer around. As long as you design your program so that nobody other than the `std::unique_ptr` holder believes they have ownership/control the pointer lifetime then you're fine. The gurus (like Sutter) actually advise that you design your functions so that they take arguments by 'normal' pointer, not smart pointer unless they really need to be aware of ownership. – Mark Dec 31 '13 at 21:48
  • 2
    Also - yes `std::shared_ptr` has a performance hit, but please profile your code before you decide that this is significant; most of the time it isn't in practice. Remember to pass `std::shared_ptr`s by const reference whenever possible to avoid copying. – Mark Dec 31 '13 at 21:50
  • It is unlikely that the copy of the shared_ptr will be noticeable, especially since you indicated the object is large. Passing shared_ptr by value will properly increment the reference count indicating how many copies are in use. This can improve debugging. weak_ptr is also a useful class for these types of operations. – TimDave Jan 01 '14 at 22:26

2 Answers2

0

Previously I had recommended a singleton, but there are is a fair amount of criticism of using that pattern - one example.

If you want something more exotic (perhaps you need to store some kind of local state in your classes), you could embed your own reference counting in the object itself:

// Psuedo code
class Resource
{
    static unsigned _refCount;

public:
    Resource() {++_refCount;}
    ~Resource() {--_refCount;  if(_refCount == 0) // cleanup!;}
};

You will notice this is basically the same scheme as a shared_ptr, but it would allow using items on the stack via instantiation instead of passing pointers around (you will have to store the resource in a static member, also, for all instances to have access to it). As one of the comments suggested, you should prefer shared_ptr and only reject it if you can verify via profile that it is too slow for your needs.

tryptik
  • 36
  • 3
  • 1
    -1 for suggesting singleton. see [this](http://jalf.dk/blog/2010/03/singletons-solving-problems-you-didnt-know-you-never-had-since-1995/) or [this](http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons) – kfsone Dec 31 '13 at 23:04
  • Removed singleton reference. – tryptik Jan 01 '14 at 22:29
  • This looks good, however there are other ways of instantiation then the default constructor, one would have to handly copy and assignment initalisation too. – dronus Jan 02 '14 at 19:25
0

The answer to this question depends on the lifetime policy you are trying to achieve.

If you want reference counting so resources are deallocated when the last handle goes out of scope then shared_ptr and intrusive_ptr are the only options that I'm aware of. You could handroll something, but I'd think long and hard before doing that. If your program is single threaded then the easiest thing to do may be to use Boost shared pointers with the atomic counting disabled:

#define BOOST_SP_DISABLE_THREADS
#include <boost/shared_ptr.hpp>

That should reduce the cost significantly. Of course the comment above is correct that before going this route you would normally profile the code to make sure it really is a bottleneck, but that discussion is a bit of a digression from the question you asked.

If, instead of reference counting, you are OK with the resources being persistent you could use a static cache:

// In the header.
#include <memory>
#include <functional>

namespace detail {
  using deleter_t = std::function<void (void *)>;
  using  handle_t = std::unique_ptr<void, deleter_t>;

  void *search_persistent_cache(std::string const &id);
  void  add_to_persistent_cache(std::string const &id, void *);
}

template<typename T, typename... Args>
T &get_persistent_resource(std::string const &id, Args&&... args) {
  auto resource = search_persistent_cache(id);

  if(resource) {
    return *static_cast<T*>(resource);
  }

  auto uniq_instance = std::unique_ptr<T>(std::forward<Args>(args)...);
  auto  raw_instance = uniq_instance.get();

  detail::deleter_t deleter = [](void *resource) -> void {
    delete static_cast<T*>(resource); 
  };

  try {
    add_to_persistent_cache(id,
        detail::handle_t { uniq_instance.release(), std::move(deleter); });
  } catch(...) {
    delete instance;
    return nullptr;
  }
}

That should be enough for the general idea. I will leave it up to you to implement the static storage and undefined functions in a source file. There are some tweaks that you can make, such as if you don't need the resources indexed by an identifier or you want to avoid using void* so debug builds can work using boost::polymorphic_down_cast.

I can't think of any other lifetime management strategies that are not significantly more complex than the ones shown above.

Chris Hayden
  • 1,104
  • 6
  • 6
  • The cache would be no good replacement for RAII pnriciples, as it would as you say make the ressources permanent. But I need deallocation, as there are many allocations too and getting new ressources would require to release some of them at some point. The `shared_ptr` is close to what I want, but it doesn't apply to referenced/copied objects, but only a copied/referenced ptr to an object. So the object has to be allocated on the heap, which is what I like to prevent. – dronus Jan 02 '14 at 19:29
  • I like to pass the ressource holding object itself around by assignment and copying, as it is very small and should not need heap allocation. – dronus Jan 02 '14 at 19:29
  • If you want the object to be persistent after exiting the scope in which it was created then you are going to have to heap allocate. There's no other way to do it. As far as having objects exist until the last reference to them goes out of scope, it sounds like you're asking for garbage collection. I believe the most recent standard makes this theoretically possible, but I've never seen an implementation of it. Outside of GC, you can't do what you're asking. Note that the problem does not apply to copied objects since the lifetime of the copy is independent of the original. – Chris Hayden Jan 03 '14 at 13:00
  • I talk about objects passed around, which definitely encloses copying and assigning them, as `return` for example would do. What is NOT copied is the unerdlying ressource. So it has to be handled correctly, which is the reason for my question. – dronus Jan 03 '14 at 14:55