Acceptable way to expose an object manager's objects? - c++

Scenario is, I'm building a game with a TextureManager class that is responsible for loading and managing textures. Game objects implementing IVisibleGameObject will need a pointer/reference to a texture from the TextureManager.
Texture manager is implemented with a std::map<std::string, boost::shared_ptr<Texture> > to hold its objects internally.
I'm not certain how I should actually expose the textures, and thought of a few possibilities, each with their own downsides:
1) const Texture& GetTex(std::string textureKey)
I feel would be ideal, but I'd like to indicate that a texture was not found in the map by returning NULL. (Second guessing myself...IS this appropriate?)
2) shared_ptr<const Texture> GetTex(std::string textureKey)
Here I could return a null shared_ptr, but I'm uncomfortable with the implication it's a shared object now. The TextureManager is the object's owner, it's the manager after all. However, considering the fact an IVisibleGameObject holds a reference/pointer to the returned Texture and depends on its existence to function correctly, isn't it also an owner of the object, and maybe shared ownership is appropriate?
3) const Texture* GetTex(stD::string textureKey)
Obviously, this is the wrong answer.
Would love for someone to clear this up for me, perhaps there is something I haven't considered.

I would stick with the standard library idiom and simply expose find and end. Then you can be perfectly efficient:
auto it = my_objects.find("foo");
if (it == my_objects.end())
{
// handle "not found"
}
else
{
it->second->do_magic();
}
The standard library already has a perfectly serviceable, generic idiom for handling collections and signaling presence or absence of elements, as well as combining insert-new-or-return-existing semantics. Why reinvent the wheel...

What I'd do is have the TextureManager hold the single shared pointer to each texture and make the GameObjects hold weak pointers. That way you can be sure you freed everything properly on exit (guaranteed no cycles).

Related

How do I go about allowing an object of a class type to be uninitialized and identifying when this is the case without using a pointer?

I recently found out for the past few years of me using C++, I have been using pointers far too often and usually when I could easily substitute them for something more appropriate. Something I used them for was using one to allow an object to be uninitialized, and easily check so.
For example, let's say I have a camera that I want to be attaching to an object in a game:
Class Camera {
public:
Entity *attachEntity;
Camera() {
attachEntity = nullptr;
}
void update() {
// If there's an entity to be attached to
if (attachEntity != nullptr) {
...
}
}
};
Is this a bad usage of a pointer? I can't find a good way of doing this without using one. And if it's not supposed to be attached to the entity, you can just set it to nullptr again if it's a pointer. Otherwise, there needs to always be an attachEntity, despite whether or not the camera is attached to it at the moment. Is there anything wrong with this practice? Is there a good way to do this without pointers? I saw that you can't just set an object to NULL like you can a pointer. What's the best way of doing this?
If you want value semantics but still need to be able to omit a
value, you can use std::optional.
You get similar behavior with a std::unique_ptr, except that
the Entity takes up no space when absent but otherwise goes into its
own block of dynamically-allocated memory.
If you want non-owning reference to an Entity instance whose
lifespan will exceed that of the Cameria instance, a raw pointer is
appropriate.
When the Entity is potentially used by multiple Cameras (or other
things) and you need to ensure its lifespan is extended until its
last use, that's where std::shared_ptr fits in.
As Michael pointed out, there's also std::weak_ptr, which is
good to know about but typically rare in practice. It's used to
address a shared_ptr without keeping it alive.
(edited to add #5)
Yes.
Use std::shared_ptr which auto-destructs when no longer needed.
And then you forget everything about copy/move constructors in your classes.
Also it initializes itself to nullptr automatically; No need for your constructor then.
You can also use std::unique_ptr if you are sure that your pointer isn't going to be duplicated, for example, when you only have one object of type Camera. Usually I use shared_ptr to allow copies over threads etc.
Class Camera {
public:
shared_ptr<Entity> attachEntity;
void update() {
// If there's an entity to be attached to
if (attachEntity) {
...
}
}
};
Generally, raw pointers are only nowadays useful to call pointer-only functions, like WinAPI etc. That way, your classes won't need copy/move constructors or assignment operators or destructors, unless you want to move/dup an object which is not visible to the language, like a HANDLE in Windows.
Since you want to attach the camera to one specific entity and change the attachment, you need a pointer.
Since the existence of that entity should be independent of the camera, a raw pointer is appropriate.
If all your entities are shared (as shared_ptrs), a weak_ptr would be best, but it’s not obvious that they should be.
Your solution for the camera is perfectly valid. All the other answers take a long way around of telling you you're doing the right thing by pointing you to shared pointers and optional types, but storing the attach entity as a pointer and using it the way you are is exactly how you would expect it to be done.
I'm going to have some haters, but I personally HIGHLY encourage the use of raw pointers (instead of smart/shared pointers) for real-time applications :).

Tracking down owner of a shared_ptr?

In our app we're about to (finally..) switch from raw pointers to using C++11 smart_ptr templates.
We do have the occasional bug in our app with (non C++) objects still keeping references to our C++ objects causing crashes in the past when accessing the then-dealloc'd objects.
Not sure if this is a silly question - but is there a way to take advantage of the smart_ptr objects and 'dump' the objects still holding on to the C++ objects when none are expected to hold a reference to one any more?
I guess what I'm asking for is a way to list all owners of smart_ptr<MyClass> at a certain point in time.
Any suggestions much appreciated!
No. Without creating your own smart pointer classes that wrap std::unique_ptr and std::shared_ptr (ignore the deprecated std::auto_ptr) that tracks this information, there is no way to do that.
The standard classes themself do not track this information (would be too costly).
Another alternative would be to modify the code of your standard library implementation to track that info. Less invasive on your code since you can keep using the standard names. But probably a bit more tricky than just wrapping the classes and use the wrappers.
Don't believe this is possible for any out of the box c++ smart pointers. You can trivially wrap a shared_ptr to achieve the same effect though.
template<typename T>
class mySmartPtr : boost::noncopyable{
public:
// This method should be the only way this object can be copied
// as the copy constructors are made private
// The newOwnerName parameter can be used to populate m_onwers.
static mySmartPtr<T> newOwner(mySmartPtr<T>&, std::string newOnwerName);
private:
std::shared_ptr<T> m_ptr;
static std::vector<std::string> m_owners;
};
We do have the occasional bug in our app with (non C++) objects still keeping references to our C++ objects causing crashes in the past when accessing the then-dealloc'd objects.
This is serious and must not be ignored.
If you're giving 3rd party library components observer status on your objects, then it stands to reason that the 3rd party component's observer must not outlive your object.
There are 3 common causes for this problem:
Improper lifetime management of the 3rd-party component (you're deleting the observed object before shutting down the 3rd party observer)
improperly detected crossing cases (resulting in 1, above)
In the case were you are the component, you must take orders on from the 3rd party framework as to when you may dispose of your object.
Before involving shared_ptrs you ought to first prove that the shared_ptr may legitimately destroy the object. If the 3rd party component has a 'deregister' method, then you can solve this by:
Ensure ownership of the 3rd party component and your observed object are controlled by the same shared_ptr, or
Use a custom deleter on the shared_ptr to cause de-registration on your controlled object on the 3rd party object before finally deleting it.
If none of this is clear, it's time to take a good long look at your object lifetimes. It often helps to draw sequence diagrams.

C++11 Smart Pointer Semantics

I've been working with pointers for a few years now, but I only very recently decided to transition over to C++11's smart pointers (namely unique, shared, and weak). I've done a fair bit of research on them and these are the conclusions that I've drawn:
Unique pointers are great. They manage their own memory and are as lightweight as raw pointers. Prefer unique_ptr over raw pointers as much as possible.
Shared pointers are complicated. They have significant overhead due to reference counting. Pass them by const reference or regret the error of your ways. They're not evil, but should be used sparingly.
Shared pointers should own objects; use weak pointers when ownership is not required. Locking a weak_ptr has equivalent overhead to the shared_ptr copy constructor.
Continue to ignore the existence of auto_ptr, which is now deprecated anyhow.
So with these tenets in mind, I set off to revise my code base to utilize our new shiny smart pointers, fully intending to clear to board of as many raw pointers as possible. I've become confused, however, as to how best take advantage of the C++11 smart pointers.
Let's assume, for instance, that we were designing a simple game. We decide that it is optimal to load a fictional Texture data type into a TextureManager class. These textures are complex and so it is not feasible to pass them around by value. Moreover, let us assume that game objects need specific textures depending on their object type (i.e. car, boat, etc).
Prior, I would have loaded the textures into a vector (or other container like unordered_map) and stored pointers to these textures within each respective game object, such that they could refer to them when they needed to be rendered. Let's assume the textures are guaranteed to outlive their pointers.
My question, then, is how to best utilize smart pointers in this situation. I see few options:
Store the textures directly in a container, then construct a unique_ptr in each game object.
class TextureManager {
public:
const Texture& texture(const std::string& key) const
{ return textures_.at(key); }
private:
std::unordered_map<std::string, Texture> textures_;
};
class GameObject {
public:
void set_texture(const Texture& texture)
{ texture_ = std::unique_ptr<Texture>(new Texture(texture)); }
private:
std::unique_ptr<Texture> texture_;
};
My understanding of this, however, is that a new texture would be copy-constructed from the passed reference, which would then be owned by the unique_ptr. This strikes me as highly undesirable, since I would have as many copies of the texture as game objects that use it -- defeating the point of pointers (no pun intended).
Store not the textures directly, but their shared pointers in a container. Use make_shared to initialize the shared pointers. Construct weak pointers in the game objects.
class TextureManager {
public:
const std::shared_ptr<Texture>& texture(const std::string& key) const
{ return textures_.at(key); }
private:
std::unordered_map<std::string, std::shared_ptr<Texture>> textures_;
};
class GameObject {
public:
void set_texture(const std::shared_ptr<Texture>& texture)
{ texture_ = texture; }
private:
std::weak_ptr<Texture> texture_;
};
Unlike the unique_ptr case, I won't have to copy-construct the textures themselves, but rendering the game objects is expensive since I would have to lock the weak_ptr each time (as complex as copy-constructing a new shared_ptr).
So to summarize, my understanding is such: if I were to use unique pointers, I would have to copy-construct the textures; alternatively, if I were to use shared and weak pointers, I would have to essentially copy-construct the shared pointers each time a game object is to be drawn.
I understand that smart pointers are inherently going to be more complex than raw pointers and so I'm bound to have to take a loss somewhere, but both of these costs seem higher than perhaps they should be.
Could anybody point me in the correct direction?
Sorry for the long read, and thanks for your time!
Even in C++11, raw pointers are still perfectly valid as non-owning references to objects. In your case, you're saying "Let's assume the textures are guaranteed to outlive their pointers." Which means you're perfectly safe to use raw pointers to the textures in the game objects. Inside the texture manager, store the textures either automatically (in a container which guarantees constant location in memory), or in a container of unique_ptrs.
If the outlive-the-pointer guarantee was not valid, it would make sense to store the textures in shared_ptr in the manager and use either shared_ptrs or weak_ptrs in the game objects, depending on the ownership semantics of the game objects with regards to the textures. You could even reverse that - store shared_ptrs in the objects and weak_ptrs in the manager. That way, the manager would serve as a cache - if a texture is requested and its weak_ptr is still valid, it will give out a copy of it. Otherwise, it will load the texture, give out a shared_ptr and keep a weak_ptr.
To summarize your use case:
*) Objects are guaranteed to outlive their users
*) Objects, once created, are not modified (I think this is implied by your code)
*) Objects are reference-able by name and guaranteed to exist for any name your app will ask for (I'm extrapolating -- I'll deal below with what to do if this is not true.)
This is a delightful use case. You can use value semantics for textures throughout your application! This has the advantages of great performance and being easy to reason about.
One way to do this is have your TextureManager return a Texture const*. Consider:
using TextureRef = Texture const*;
...
TextureRef TextureManager::texture(const std::string& key) const;
Because the underling Texture object has the lifetime of your application, is never modified, and always exists (your pointer is never nullptr) you can just treat your TextureRef as simple value. You can pass them, return them, compare them, and make containers of them. They are very easy to reason about and very efficient to work on.
The annoyance here is that you have value semantics (which is good), but pointer syntax (which can be confusing for a type with value semantics). In other words, to access a member of your Texture class you need to do something like this:
TextureRef t{texture_manager.texture("grass")};
// You can treat t as a value. You can pass it, return it, compare it,
// or put it in a container.
// But you use it like a pointer.
double aspect_ratio{t->get_aspect_ratio()};
One way to deal with this is to use something like the pimpl idiom and create a class that is nothing more than a wrapper to a pointer to a texture implementation. This is a bit more work because you'll end up creating an API (member functions) for your texture wrapper class that forward to your implementation class's API. But the advantage is that you have a texture class with both value semantics and value syntax.
struct Texture
{
Texture(std::string const& texture_name):
pimpl_{texture_manager.texture(texture_name)}
{
// Either
assert(pimpl_);
// or
if (not pimpl_) {throw /*an appropriate exception */;}
// or do nothing if TextureManager::texture() throws when name not found.
}
...
double get_aspect_ratio() const {return pimpl_->get_aspect_ratio();}
...
private:
TextureImpl const* pimpl_; // invariant: != nullptr
};
...
Texture t{"grass"};
// t has both value semantics and value syntax.
// Treat it just like int (if int had member functions)
// or like std::string (except lighter weight for copying).
double aspect_ratio{t.get_aspect_ratio()};
I've assumed that in the context of your game, you'll never ask for a texture that isn't guaranteed to exist. If that is the case, then you can just assert that the name exists. But if that isn't the case, then you need to decide how to handle that situation. My recommendation would be to make it an invariant of your wrapper class that the pointer can't be nullptr. This means that you throw from the constructor if the texture doesn't exist. That means you handle the problem when you try to create the Texture, rather than to have to check for a null pointer every single time you call a member of your wrapper class.
In answer to your original question, smart pointers are valuable to lifetime management and aren't particularly useful if all you need is to pass around references to object whose lifetime is guaranteed to outlast the pointer.
You could have a std::map of std::unique_ptrs where the textures are stored. You could then write a get method that returns a reference to a texture by name. That way if each model knows the name of its texture(which it should) you can simple pass the name into the get method and retrieve a reference from the map.
class TextureManager
{
public:
Texture& get_texture(const std::string& key) const
{ return *textures_.at(key); }
private:
std::unordered_map<std::string, std::unique_ptr<Texture>> textures_;
};
You could then just use a Texture in the game object class as opposed to a Texture*, weak_ptr etc.
This way texture manager can act like a cache, the get method can be re-written to search for the texture and if found return it from the map, else load it first, move it to the map and then return a ref to it
Before I get going, as I accidentally a novel...
TL;DR Use shared pointers for figuring out responsibility issues, but be very cautious of cyclical relationships. If I were you, I would use a table of shared pointers to store your assets, and everything that needs those shared pointers should also use a shared pointer. This eliminates the overhead of weak pointers for reading (as that overhead in game is like creating a new smart pointer 60 times a second per object). It's also the approach my team and I took, and it was super effective. You also say your textures are guaranteed to outlive the objects, so your objects cannot delete the textures if they use shared pointers.
If I could throw my 2 cents in, I'd like to tell you about an almost identical foray I took with smart pointers in my own video game; both the good and the bad.
This game's code takes an almost identical approach to your solution #2: A table filled with smart-pointers to bitmaps.
We had some differences though; we had decided to split our table of bitmaps into 2 pieces: one for "urgent" bitmaps, and one for "facile" bitmaps. Urgent bitmaps are bitmaps that are constantly loaded into memory, and would be used in the middle of battle, where we needed the animation NOW and didn't want to go to the hard disk, which had a very noticeable stutter. The facile table was a table of strings of file paths to the bitmaps on the hdd. These would be large bitmaps loaded at the beginning of a relatively long section of
gameplay; like your character's walking animation, or the background image.
Using raw pointers here has some problems, specifically ownership. See, our assets table had a Bitmap *find_image(string image_name) function. This function would first search the urgent table for the entry matching image_name. If found, great! Return a bitmap pointer. If not found, search the facile table. If we find a path matching your image name, create the bitmap, then return that pointer.
The class to use this the most was definitely our Animation class. Here's the ownership problem: when should an animation delete its bitmap? If it came from the facile table then there's no problem; that bitmap was created specifically for you. It's your duty to delete it!
However, if your bitmap came from the urgent table, you could not delete it, as doing so would prevent others from using it, and your program goes down like E.T. the game, and your sales follow suit.
Without smart pointers, the only solution here is to have the Animation class clone its bitmaps no matter what. This allows for safe deletion, but kills the speed of the program. Weren't these image supposed to be time sensitive?
However, if the assets class were to return a shared_ptr<Bitmap>, then you have nothing to worry about. Our assets table was static you see, so those pointers were lasting until the end of the program no matter what. We changed our function to be shared_ptr<Bitmap> find_image (string image_name), and never had to clone a bitmap again. If the bitmap came from the facile table, then that smart pointer was the only one of its kind, and was deleted with the animation. If it was an urgent bitmap, then the table still held a reference upon Animation destruction, and the data was preserved.
That's the happy part, here's the ugly part.
I've found shared and unique pointers to be great, but they definitely have their caveats. The largest one for me is not having explicit control over when your data gets deleted. Shared pointers saved our asset lookup, but killed the rest of the game on implementation.
See, we had a memory leak, and thought "we should use smart pointers everywhere!". Huge mistake.
Our game had GameObjects, which were controlled by an Environment. Each environment had a vector of GameObject *'s, and each object had a pointer to its environment.
You should see where I'm going with this.
Objects had methods to "eject" themselves from their environment. This would be in case they needed to move to a new area, or maybe teleport, or phase through other objects.
If the environment was the only reference holder to the object, then your object couldn't leave the environment without getting deleted. This happens commonly when creating projectiles, especially teleporting projectiles.
Objects also were deleting their environment, at least if they were the last ones to leave it. The environment for most game states was a concrete object as well. WE WERE CALLING DELETE ON THE STACK! Yeah we were amateurs, sue us.
In my experience, use unique_pointers when you're too lazy to call delete and only one thing will ever own your object, use shared_pointers when you want multiple objects to point to one thing, but can't decide who has to delete it, and be very wary of cyclical relationships with shared_pointers.

Implications of modeling ownership using smart pointers

I am currently manually managing the lifecycle of objects in my project. I am considering switching to smart pointers, specifically tr1::shared_pointer and tr1::weak_ptr. However, I am seeing a couple of problems and would like to get some input on best practices.
Consider the following class diagram:
In this diagram, the thick arrows represent associations with an ownership semantics (the source is responsible for deleting the target or targets). The thin arrows represent associations without ownership.
From what I understand, one way of implementing an association with ownership semantics would be to use tr1::shared_ptr (or a collection thereof). Other associations can be implemented using either tr1::shared_ptr or tr1::weak_ptr. The former is prohibited if it may result in circular references because that would prevent proper release of resources.
As you can see, there is a circle of associations between classes Edge and Side. I can easily break this by implementing the "left" and "right" associations from Edge to Side using tr1::weak_ptrs. However, I would prefer using the smart pointers to document, in code, the ownership semantics of the associations. So I would like to use shared_ptrs only for the associations represented by thick arrows in the diagram, and weak_ptrs for everything else.
Now my first question is: Should I use weak_ptrs liberally as described above, or should I use them as little as possible (only to avoid circular references)?
The next question is: Suppose I have a function that computes the average of a set of vertices. Suppose further that I have implemented the association from the Polyhedron to its Vertices like so:
class Vertex;
class Polyhedron {
protected:
std::vector<std::tr1::shared_ptr<Vertex> > m_vertices;
};
and that I have implemented the association from a Side to its Vertices like so:
class Vertex;
class Side {
protected:
std::vector<std::tr1::weak_ptr<Vertex> > m_vertices;
};
Note that the set of vertices of a Side is a subset of the set of vertices of a Polyhedron. Now let's say I have a function that computes the average of a set of vertices. The function is currently declared like so:
const Vertex centerOfVertices(std::vector<Vertex*> vertices);
Now if I represent the associations as above, I suddenly need two functions if I understand everything correctly:
const Vertex centerOfVertices(std::vector<std::tr1::shared_ptr<Vertex> > vertices);
const Vertex centerOfVertices(std::vector<std::tr1::weak_ptr<Vertex> > vertices);
Because I cannot convert between a vector of shared_ptr and a vector of weak_ptr. This smells funny to me. I would like to know what approach I should take here to avoid such a situation.
However, I would prefer using the smart pointers to document, in code, the ownership semantics of the associations.
As you should. That’s what they express perfectly, after all.
So I would like to use shared_ptrs only for the associations represented by thick arrows in the diagram, and weak_ptrs for everything else.
Go for it, with one caveat: your ownership doesn’t look at all like shared ownership, it’s simple, unique ownership. Consequently, the appropriate smart pointer here isn’t shared_ptr but rather std::unique_ptr, and the weak pointers would then simply be raw pointers.
Now if I represent the associations as above, I suddenly need two functions …
Yes. Well, or you use templates.
Alternatively, since the function isn’t actually interested in sharing ownership at all, you could pass raw pointers to it, but this would of course imply first getting raw pointers from your structure, which requires an added step for the client, which may not be good for the interface.
Using shared pointers seems sensible, especially as you may find that vertices etc. can be shared between polyhedra.
To convert from a vector of weak pointers to a vector of shared pointers, use an explicit constructor:
centerOfVertices(std::vector<std::tr1::shared_ptr<Vertex> >(vertices.begin(), vertices.end()));
You certainly can also use shared_ptr for cyclic references. There are some rare situations where this actually makes sense. However, you must take care to break the cycle once you are done with the objects.

Is it wise to provide access to weak_ptr in a library interface?

I have written a library that exposes references to several related object types. All of these objects have their lifetimes managed by the library internally via boost::shared_ptr
A user of the library would also be able to know, by nature of the library, the lifetimes of any of the exposed objects. So they could store pointers or keep references to these objects. It would be reasonable for them to do this and know when those objects are no longer valid.
But I feel guilty forcing my users to be reasonable.
Is it acceptable to have a library expose weak_ptr's to its objects? Have other libraries done this?
I have profiled this library's usage in apps and have found it to be too mission-critical to expose weak_ptr exclusively.
Would it be wiser to have matching API functions expose either a reference or a weak_ptr or to make any object capable of exposing a weak_ptr to itself?
If the smart_ptrs are already directly accessible to the library's users, then they've already got access to the weak_ptrs, simply via the corresponding weak_ptr's constructor. But if the smart_ptrs are all internal to the library, that's a different story.
In that case, I'd recommend letting each object pass out weak_ptrs to itself, in addition to any other access your library offers. That gives the users the most flexibility: if they need a weak_ptr, they've got immediate access to it; if they need a shared_ptr, they can easily get it; and if they just need access to the object itself, they can ignore the smart pointers entirely.
Of course, I don't know what your library does or how it's used or designed. That might change my recommendation.
Coming up with convoluted mechanisms to get at the objects of your library will only result in people not using your library. If the semantics of the library dictate you need to have people using weak_ptrs, there no way around the user knowing that the objects may go away at some point. Make the interface express as much information about the usage of the library as possible, keeps documentation down and makes it infinitely easier to use.
You can't design around bad/inexperienced users.
If you give your clients access to weak_ptrs they can just lock them to create shared_ptrs and end up delaying the destruction of objects. That might cause problems with your library.
I suggest wrapping a weak_ptr in some other class and giving the caller a shared_ptr to that. That way they can't just call weak_ptr<T>::lock(). You seem to have performance constraints that might influence how you implement it, but a shared_ptr<InterfaceClass> might be a good way to go, and keep the class with the weak_ptr internal to your library.
That way you also keep these implementation details out of your library interface and you can change the way you implement it without changing your interface.
I don't see any problem with exposing weak_ptrs, especially given that TR1 has similar smart pointers (PDF).
TR1 is largely implemented by Visual Studio and GCC, but not some of the other compilers out there. But when it's implemented in all the compilers you care about, you may want to rework the API to expose those smart pointers instead.
If you want to both trap invalid use of the library (trying to access the objects when they have been deleted) as well as have a high-performance API (no weak_ptr's and shared_ptr's in the API), then you could consider have a different API for debug and nondebug builds.
Let's suppose for simplicity that you have only one class of objects you expose; call this class Object. The pointer type which you return from the API for accessing the internal objects is then defined as:
#ifdef DEBUG
typedef ObjectPtrFacade ObjectPtr
#else
typedef Object * ObjectPtr;
#endif
Here the facade is class which you write. It works roughly like this:
class ObjectPtrFacade {
public:
ObjectPtrFacade(Object *o) : wptr(o) { }
// copy constructor and assignment here etc. (not written)
Object * operator -> () const { return access(); }
Object & operator * () const { return *access(); }
private:
Object * access() {
assert(wptr.use_count() > 0);
return (Object *)(wptr.lock());
}
weak_ptr<Object> wptr;
}
In this way, whenever you build a debugging build, you have a special kind of smart pointer in use which asserts before accessing the object that its use_count() is higher than zero, i.e. that the object still exists. If the object has been released, you get a failing assert, which is better than a null pointer reference.
In general of course it is so that using weak_ptr's does not help if you have "stupid" users of the API, because they could call lock() and then still make a null-pointer reference after the weak_ptr returns a shared_ptr which is empty...