My thoughts:
- Don't return references to resources. This is a pain in the butt for the user. You can solve the "null resource is a valid 1x1 resource" issue just the same with pointers. Returning references means
TextureResource &tex = texMgr->GetTexture("foo"); if(!tex.CanBeRenderTarget()) { // Oops, try a different texture. tex = texMgr->GetTexture("foo2"); } // use tex as a render target
does not do want you probably want, since references cannot be reseated. One part of resource management is given the resource "handles" (whatever they are) value semanatics so that the user can pay more attention to the use of the resource, rather than the management of the lifetime of its variable.
- boost::shared_ptr<> works wonderfully for this. Typedefs like:
typedef boost::shared_ptr< Texture> TextureH;
allow the texture handles to be treated basically as value types (excepting the use of operator-> to access them, which I prefer to . anyway, visually). The reference counting is done for you (no need to manually have users add or recall references), and if you ever want to switch to a different wrapper than shared_ptr, all you need to supply is the appropriate operator-> and the rest of your code should work fine (unless you invoke shared_ptr member functions anywhere). I've had success with a lightweight wrapper around shared_ptr as well (to prevent invocation of undesired shared_ptr methods as well as some other things).
- There's a point at which you can't stop the user from doing really stupid stuff. And well before that point, you'll be optimizing for the rare case when the user is doing something slightly stupid, and that's a waste of your time. If you are not afraid of the friend keyword, you could make your constructors and operator= private (leave the destructor public) and designate friendship to the manager. That way the user cannot dereference your pointer/handle/what have you an obtain a regular Resource from a Resource* (etc), at it would invoke the copy ct or operator= and cause a compiler error.