Suppose I had a Manager Class that held a vector of some object:
class SomeObjectManager
{
private:
std::vector<SomeObject> _heldObjects;
};
And in that class I had some function that iterated through said vector to return the requested object.
SomeObject getSomeObjectByName(std::string nameToFind);
What I need to know is when is it proper to use smart pointers. Should I actually be returning something like below?
std::shared_ptr<SomeObject> getSomeObjectByName(std::string nameToFind);
Or should I be using something else like unique_ptr or weak_ptr? I want the SomeObjectManager class to own the actual object being returned and never have said SomeObject be deleted unless the Manager makes it so.
I have only recently came back to the C++ world after being in C# mode for quite some time; thanks for the help and clearing up my confusion.
I have read a lot about this matter but never really found a straight answer to my particular situation.
Edit #1
I'd like to reword my last few sentences with this:
I want the SomeObjectManager class to own the actual object being returned and never have said SomeObject be removed from the vector and subsquently deleted, fall out of scope, until the Manager forces it to do so. For example:
void SomeObjectManager::removeSomeObjectByName(const std::string& objectToRemove);
This would just iterate over the vector, finding said SomeObject, and remove it from the Vector.
Since SomeObjectManager is the owner of the SomeObject instances (stored in its std::vector data member), I'd just return raw pointers, since they are actually observing pointers.
std::vector<SomeObject> _heldObjects;
SomeObject* getSomeObjectByName(const std::string& nameToFind) {
... find index of object corresponding to 'nameToFind'
return &_heldObjects[foundIndex];
}
(Note that I passed nameToFind using reference to const, since I assume that nameToFind is an input string, so if inside the method you are just observing that string, you can avoid deep-copies using const &).
You must pay attention when you have owning raw pointers (they should be wrapped inside safe RAII boundaries), but observing raw pointers are fine.
Just make sure that the lifetime of SomeObjectManager exceeds that of its clients, to make sure that the clients are referencing valid objects.
Note also that if you add new items to the vector data member (e.g. using std::vector::push_back()), the addresses of the previous SomeObject instances stored in the vector can change. So, if you gave pointers to those outside, they become invalid.
So, make sure that the vector size and vector content are not changed before you give pointers to its elements to client code outside.
An alternative would be to have std::vector<std::unique_ptr<SomeObject>> as data member. In this case, even if the vector is resized, the addresses you returned using the smart pointers (in particular using std::unique_ptr::get()) are still valid:
std::vector<std::unique_ptr<SomeObject>> _heldObjects;
SomeObject* getSomeObjectByName(const std::string& nameToFind) {
... find index of object corresponding to 'nameToFind'
return _heldObjects[foundIndex].get();
}
PS
Another option might be returning references to const SomeObject (assuming that this use of const makes sense in your design):
std::vector<SomeObject> _heldObjects;
const SomeObject& getSomeObjectByName(const std::string& nameToFind) const {
... find index of object corresponding to 'nameToFind'
return _heldObjects[foundIndex];
}
If your program runs in a single thread, you are mostly good with returning raw pointers or references to the objects that are stored in the vector, if you have sufficient discipline.
Since the manager privately owns the vector and the objects inside, and thus controls when objects are deleted, you can make sure that there remain no invalid pointers to objects that have been deleted (this isn't automatically guaranteed!).
Basically, the manager must only ever delete objects when it knows that nobody holds a reference to that object, for example by only doing this at distinct, well-defined times (such as at program end, or when it knows that no consumers remain, or such).
Reference counting is one way of doing that, and it is what shared_ptr does internally, too (well, no... strictly to the letter of the specification, reference counting isn't required, only the visible behavior is defined, but practially all implementations do it).
The process of "deleting" an object would thus merely decrement the reference counter (much like in a managed language) and the object would really cease to exist when the reference counter reaches zero. Which is probably but not necessarily immediately the case when you "delete" it. It might as well take a few moments before the object is actually destroyed.
That approach works "automatically" without a lot of diligence and rigid discipline, and it can be implemented simply by storing shared_ptrs of your objects in the vector and returning either shared_ptrs or weak_ptrs.
Wait a moment! Then why are there even weak_ptrs if you can just return a shared_ptr too? They do different things, both logically and practically. shared_ptrs own (at least partially), and weak_ptrs do not. Also, weak_ptrs are cheaper to copy.
In a multi-threaded program, storing shared_ptrs and returning a weak_ptr is the only safe approach. The weak_ptr does not own the resource and thus cannot prevent the manager from deleting the object, but it gives the holder a reliable and unambiguous way of knowing whether the resource is valid and that the resource will remain valid while you use it.
You return that weak_ptr, and when the consumer actually wants to use the object, it converts the weak_ptr to a temporary shared_ptr. This will either fail (giving a null pointer) so the consumer knows that the object has been deleted, and it may not use it. Or, it will succeed, and now the consumer has a valid pointer with shared ownership of an object which is now guaranteed to remain valid while it is being used.
There is nothing in between "valid" and "invalid", no guessing, and nothing that can fail. If you successfully converted to a valid temporary shared_ptr, you are good to go. Otherwise, the object is gone, but you know that.
This is a big, big plus in terms of safety. Even if the manager "deletes" the object while you are using it, your program will not crash or produce garbage, the object remains valid until you stop using it!
Arguably, this somewhat blurs the "the manager deletes objects when it chooses to do so" paradigm, but it really is the only way of doing it safely. The manager is still the one in control of what objects to delete, it only cannot delete an object immediately while it is in use (which would possibly result in a terrible desaster). It can, however, at any time schedule the deletion for the next possible time by removing its shared_ptr and thus decrementing the reference count.
The only obvious showstopper is the case where an object must be destroyed immediately (because the destructor has side effects that must happen immediately without delay). But in this case, it is very hard (a nightmare!) to get concurrent access right. Luckily, that's a very rare scenario, too.
Return a reference (or regular pointer) to the SomeObject from your function. The reference is valid so long as it remains in the vector, and the vector is not reallocated (careful with that, maybe use a list instead or vector of unique_ptr). When removed from the vector, the object is dead and all references to it are no longer valid. (Again careful removing element in the middle)
If you are not storing your objects as std::shared_ptrs, then it wouldn't make any sense to return an std::shared_ptr. Not even sure how you are going to do it. I don't think there is a way to wrap an already existing pointer within a smart pointer. If you already have the data there, you can just return a regular const pointer to it. That way you will avoid the overhead that it takes to copy the object contents.
You have a choice of using shared_ptr or weak_ptr in this case. Which you use will depend on the lifetime you want for the object.
If you only want the object to be valid whilst the SomeObjectManager has a reference to it and a client is using it at that time then use weak_ptr. If you want a reference to remain valid if either the SomeObjectManager has a reference and a client stores a reference to it.
Here's an example with a weak_ptr.
std::weak_ptr<SomeObject> weakref = getSomeObject();
// weakref will not keep the object alive if it is removed from the object manager.
auto strongref = weakref.lock();
if ( strongref ) {
// strongref is a shared_ptr and will keep the object alive until it
// goes out of scope.
}
This can be useful in a multi-threaded environment as the shared_ptr reference count access is thread-safe. However, it does mean that a client can extend the lifetime of an object longer than you may like.
If you want to use smart shared pointers, the vector itself should use the smart pointer.
class SomeObjectManager
{
private:
std::vector<std::shared_ptr<SomeObject> > _heldObjects;
};
But then you're safe.
Related
I'm currently building a game engine, and have run into a bit of a problem. I have found a couple of solution, but it's quite as elegant as I would like them to be.
Here's the issue:
Instantiate Object A in the engine
Get a pointer to Object A - ObjectA*
Destroy Object A
ObjectA* becomes a dangling pointer
Using shared_ptr is a no-go since it can lock objects, which we don't want. There's nothing wrong with Object A being destroyed, I just need a way to check whether it has.
I have two solutions for this issue:
The first is simply to return a surrogate object whenever you want a reference to an object. This surrogate object could have an implicit conversion to a raw pointer, for cases where we know for certain the object is valid. In cases where we keep the reference around for longer, we would use the surrogate object, which is basically just a pointer to a pointer. When the object is destroyed we simply set its pointer to nullptr, which the surrogate would then be able to check for
The second solution is to not return pointers at all. Instead, whenever we want a reference to an object, we pass along the pointer we want it assigned to as a parameter. The engine will then keep said pointer in memory and manually set it to nullptr when the object is destroyed.
Here are the requirements for my preferred elegant solution:
Only uses raw pointers for references
Said raw pointers are returned from function calls (i.e. Ptr* AddCompoenent<>() instead of void AddComponent<>(Ptr*&)
Pointers will become nullptr when the object they point to is destroyed
Is this at all possible?
If you want to return simple pointers, there is no way of collecting all pointers to the object when you deallocate it and setting them to nullptr if you just use normal C++ objects.
There is of course a solution, it's not new, and it's called garbage collection. This is exactly the algorithm you'd need to do it; it analyzes the stack and heap and is able to collect all pointers to your object. Just instead of keeping the object if it finds pointers to it, you want it to set the pointers to nullptr.
Now there are a couple of prerequisites to garbage collection. One is that you must be able to recognize all pointer values on the stack and heap at runtime, so that you can be sure that some data is actually a pointer to your object and not just some integer or other value that happens to hold a value that looks like a pointer to your object. If you still want to have simple pointer types in your code, you need support from your compiler to have that information at runtime – and C++ does not provide it. This is why garbage collection is typically defined for a whole language.
Moreover, the performance implications of using this algorithm are horrendous: Every time you deallocate an object, you would need to analyze all stack and heap. This would mean it runs far more often than a normal gc-based language runs it, and look at their performance losses due to it.
I'm trying to figure out, what happens if you create a shared pointer, that points to an object that is held in a vector.
The Code looks like this:
class TestObject
{
public:
int someTestData;
};
class Test
{
public:
std::shared_ptr<TestObject> testPointer;
std::shared_ptr<std::vector<TestObject>> getTestVector()
{
return testVector;
}
private:
std::shared_ptr<std::vector<TestObject>> testVector;
};
The Problem is, that I want to both support fast iteration over all TestObjects, but also provide shared pointers to single objects in the vector.
But now I dont know what happens if the last shared ptr to an object in the vector gets deleted.
Does the object get removed from the vector?
Does this cause undefined behaviour?
How can I archieve the behaviour I want if this doesnt work?
EDIT:
To Clarify, what i need:
I need a MemoryManager Class, that has a method CreateTestObject, that creates a new TestObject, stores it in a vector or some other container that supports fast iteration and returns a some kind of shared_ptr to the newly created object so that the object that needs a new TestObject has a reference.
Now i also need the shared_ptr to the TestObject to be able to remove the TestObject from the container it is stored in, when the last shared_ptr goes out of scope, so that i'm not left with a vector full of unused TestObjects.
It's hard to know what happens on your end, since you didn't share the code which actually initializes testPointer. But what you're looking for is an aliased std::shared_ptr:
testPointer = {testVector, &(*testVector)[0]};
From then on, testPointer points to the first element of the vector, but shares the ownership of the vector itself with testVector, providing the correct semantics.
Do note that this doesn't prevent the vector from relocating its storage and making testPointer dangle if you cause it to reallocate.
For the record, trying to construct a std::shared_ptr owning the element itself, as follows:
testPointer.reset(&(*testVector)[0]); // Wrong!
... will necessarily trigger UB down the line, since std::vector has unique ownership of its elements already, and there's no way that you can make std::vector or std::shared_ptr relinquish ownership.
"I'm trying to figure out, what happens if you create a shared pointer, that points to an object that is held in a vector."
That is a very bad idea because the position of any element of a vector can change simply by adding or removing an element to the vector. What you implement is simply a duplication of ownership which brakes basic OOP concepts.
Does the object get removed from the vector?
A shared pointer is the "owner" of the object and the vector is also the owner. So it is conceptional broken!
Does this cause undefined behaviour?
It is still undefined, as you generate dangling pointes because vector can move its objects.
How can I achieve the behaviour I want if this doesnt work?
You have already fast access via operator[] and you have already "pointers" as iterators can be used as any other pointer.
You can do this using a custom deleter with your shared_ptr and using a container like boost::stable_vector:
testVector.emplace_back();
auto it = V.end()-1;
auto deleter = [&testVector, it] (int*) {testVector.erase(it);};
std::shared_ptr<TestObject> ptr(&testVector.back(), deleter);
When the last shared_ptr to an object goes out of scope, it will remove it from TestVector. Note however, that this does not work the other way round: If the object is removed from TestVector through any other means (e.g. by TestVector going out of scope), this will trigger undefined behavior, so it is your responsibility to make sure that this does not happen. Note also that this does not work with std::vector, as the iterators get invalidated when the vector is resized. You could however use std::list, although that will probably be slower than stable_vector.
This is not the cleanest solution (due to the very real possibility of triggering UB if you're not careful), but doing this proberly would be much more involved.
If I understand correctly, a weak_ptr doesn't increment the reference count of the managed object, therefore it doesn't represent ownership. It simply lets you access an object, the lifetime of which is managed by someone else.
So I don't really see why a weak_ptr can't be constructed from a unique_ptr, but only a shared_ptr.
Can someone briefly explain this?
If you think about it, a weak_ptr must refer to something other than the object itself. That's because the object can cease to exist (when there are no more strong pointers to it) and the weak_ptr still has to refer to something that contains the information that the object no longer exists.
With a shared_ptr, that something is the thing that contains the reference count. But with a unique_ptr, there is no reference count, so there is no thing that contains the reference count, thus nothing to continue to exist when the object is gone. So there's nothing for a weak_ptr to refer to.
There would also be no sane way to use such a weak_ptr. To use it, you'd have to have some way to guarantee that the object wasn't destroyed while you were using it. That's easy with a shared_ptr -- that's what a shared_ptr does. But how do you do that with a unique_ptr? You obviously can't have two of them, and something else must already own the object or it would have been destroyed since your pointer is weak.
std::weak_ptr can't be used unless you convert it to std::shared_ptr by the means of lock(). if the standard allowed what you suggest, that means that you need to convert std::weak_ptr to unique in order to use it, violating the uniqueness (or re-inventing std::shared_ptr)
In order to illustrate, look at the two pieces of code:
std::shared_ptr<int> shared = std::make_shared<int>(10);
std::weak_ptr<int> weak(shared);
{
*(weak.lock()) = 20; //OK, the temporary shared_ptr will be destroyed but the pointee-integer still has shared to keep it alive
}
Now with your suggestion:
std::unique_ptr<int> unique = std::make_unique<int>(10);
std::weak_ptr<int> weak(unique);
{
*(weak.lock()) = 20; //not OK. the temporary unique_ptr will be destroyed but unique still points at it!
}
That has been said, you may suggest that there is only one unique_ptr, and you still can dereference weak_ptr (without creating another unique_ptr) then there is no problem. But then what is the difference between unique_ptr and shared_ptr with one reference? or moreover, what is the difference between a regular unique_ptr and C-pointers an get by using get?
weak_ptr is not for "general nonowning resources", it has a very specific job - The main goal of weak_ptr is to prevent circular pointing of shared_ptr which will make a memory leak. Anything else needs to be done with plain unique_ptr and shared_ptr.
A shared_ptr basically has two parts:
the pointed-to object
the reference count object
Once the reference count drops to zero the object (#1) is deleted.
The weak_ptr needs to be able to know if the object (#1) still exists. In order to do this, it has to be able to see the reference count object (#2), if it's not zero it can create a shared_ptr for the object (by incrementing the reference count). If the count is zero it will return an empty shared_ptr.
Consider the question of when can the reference count object (#2) be deleted? We must wait until no shared_ptr OR weak_ptr object refer to it. For this purpose the reference count object holds two reference counts, a strong ref and a weak ref. The reference count object will only be deleted when both these counts are zero. This means that part of the memory can only be freed after all the weak references are gone (this implies a hidden disadvantage with make_shared).
tl;dr; weak_ptr depends on a weak reference count which is part of shared_ptr, there cannot be a weak_ptr without a shared_ptr.
Conceptually, there is nothing preventing an implementation where a weak_ptr only provides access and a unique_ptr controls the lifetime. However, there are problems with that:
unique_ptr doesn't use reference counting to begin with. Adding the management structure for managing the weak references would be possible, but require an additional dynamic allocation. Since unique_ptr is supposed to avoid any(!) runtime overhead over a raw pointer, that overhead is not acceptable.
In order to use the object referenced by a weak_ptr, you need to extract a "real" reference from it, which will first validate that the pointer is not expired first and then give you this real reference (a shared_ptr in this case). This means that you suddenly have a second reference to an object that is supposed to be uniquely owned, which is a recipe for errors. This can't be fixed by returning a mixed half-strong pointer that only temporarily delays possible destruction of the pointee, because you could just as well store that one, too, defeating the idea behind unique_ptr.
Just wondering, what problem are you trying to solve using a weak_ptr here?
Looks like everyone is writing here about std::weak_ptr but not about weak poiner concept which I believe is what the author is asking for
I think that no one mentioned why standard library is not providing weak_ptr for unique_ptr. Weak pointer CONCEPT doesn't disclaims unique_ptr usage. Weak pointer is only an information if the object has been already deleted so it's not a magic but very simple generalized observer pattern.
It's because of threadsafety and consistency with shared_ptr.
You just simply can't guarantee that your weak_ptr (created from unique_ptr existing on other thread) will be not destroyed during calling method on pointed object.
It's because weak_ptr needs to be consistent with std::shared_ptr which guarantee threadsafety. You can implement weak_ptr which works correctly with unique_ptr but only on the same thread - lock method will be unnecessary in this case. You can check chromium sources for base::WeakPtr and base::WeakPtrFactory - you can use it freely with unique_ptr.
Chromium weak pointer code is most probably based on last member destruction - you need to add factory as a last member and after that I believe that WeakPtr is informed abut object deleteion (I'm not 100% sure) - so it doesn't looks so hard to implement.
Overall using unique_ptr with weak pointer concept is OK IMHO.
No-one has mentioned the performance aspect of the problem yet, so let me throw my $0.02 in.
weak_ptr must somehow know when the corresponding shared_ptrs have all gone out of scope and the pointed object has been deallocated and destroyed. This means that shared_ptrs need to communicate the destruction towards each weak_ptr to the same object somehow. This has a certain cost – for example, a global hash table needs to be updated, where weak_ptr gets the address from (or nullptr if the object is destroyed).
This also involves locking in a multi-threaded environment, so it can potentially be too slow for some tasks.
However, the goal of unique_ptr is to provide a zero-cost RAII-style abstraction class. Hence, it should not incur any other cost than that of deleteing (or delete[]ing) the dynamically allocated object. The delay imposed by doing a locked or otherwise guarded hash table access, for example, may be comparable to the cost of deallocation, however, which is not desirable in the case of unique_ptr.
It may be useful to distinguish reasons for preferring a unique_ptr over a shared_ptr.
Performance One obvious reason is computational-time and memory use. As currently defined, shared_ptr objects typically need something like a reference-count value, which takes space and also must be actively maintained. unique_ptr objects don't.
Semantics With a unique_ptr, you as the programmer have a pretty good idea when the pointed-to object is going to be destroyed: when the unique_ptr is destroyed or when one of its modifying methods is called. And so on large or unfamiliar code bases, using unique_ptr statically conveys (and enforces) some information about the program's runtime behavior that might not be obvious.
The comments above have generally focused on the performance-based reasons that it would be undesirable for weak_ptr objects to be tied to unique_ptr objects. But one might wonder if the semantics-based argument is sufficient reason for some future version of the STL to support the use-case implied by the original question.
After many years of c++ programing works, i finally realized that the correct way in c++ world is not to use shared_ptr/weak_ptr for ever. We have spent so many time to fix the bugs caused by the unclear ownership of share_ptr.
The solution is to use unque_ptr and some weak pointer for unique_ptr instead, just as what's expected in this topic.
cpp standard library has no weak pointer for unique_ptr, so i create a very simple, but useful libary here --
https://github.com/xhawk18/noshared_ptr
It has two new smart pointers,
noshared_ptr<T>, a new kind of unique_ptr
noweak_ptr<T>, the weak pointer for noshare_ptr<T>
I demonstrated the problem to myself with a MWE implementing weak_ptr on single objects. (I implement it on X here, but X can be anything that can tell us when it dies, such as a unique_ptr with a custom deleter).
The ultimate problem however is that at some point we need reference counting on the weak pointers themselves, because while X is not shared, the weak pointers are shared. This takes us full circle back to using shared_ptr again.
Perhaps the only advantage of doing this is that the intent of single ownership is clearer and cannot be violated, however, as Stroustrup suggests and is quoted this answer, this can be hinted at with the using statement.
#include <iostream>
#include <memory>
template<typename T>
struct ControlBlock
{
T* Target;
explicit ControlBlock(T* target) : Target(target) {}
};
template<typename T>
struct WeakReference
{
std::shared_ptr<ControlBlock<T>> ControlBlock;
T* Get() { return ControlBlock ? ControlBlock->Target : nullptr; }
};
template<typename T>
struct WeakReferenceRoot
{
WeakReference<T> _weakRef;
WeakReferenceRoot(T* target) : _weakRef{std::make_shared<ControlBlock<T>>(target)} { }
const WeakReference<T>& GetReference() { return _weakRef; }
~WeakReferenceRoot() { _weakRef.ControlBlock->Target = nullptr; }
};
struct Customer
{
WeakReferenceRoot<Customer> Weak{this};
};
int main() {
WeakReference<Customer> someRef;
std::cout << "BEFORE SCOPE - WEAK REFERENCE IS " << someRef.Get() << "\n";
{
Customer obj{};
someRef = obj.Weak.GetReference();
std::cout << "IN SCOPE - WEAK REFERENCE IS " << someRef.Get() << "\n";
}
std::cout << "OUT OF SCOPE - WEAK REFERENCE IS " << someRef.Get() << "\n";
return 0;
}
Sadly as with many cases - cause the C++ committee just didn't care and dismissed such usecases.
How it is:
weak_ptr was specified in terms of shared-pointer, precluding any attempts at making it a more broadly useful smart-pointer. In C++ conceptually a weak-ptr is a non-owning pointer that MUST be converted to a shared_ptr to access the underlying object. And as a unique_ptr does not support any sort of reference-counting (as it is the unique owner by definition) converting a weak_ptr to a pointer with any sort of ownership is not allowed.
It is sadly a bit too late to get good well-named smart-pointers that offer a bit more generality.
But you can create something like that:
To make it safe you would need your own deleter (unique_ptr has the deleter in the type), and a new non-owning unique_ptr_observer that changes the deleter. When creating an observer it would register a cleanup-handler as the deleter. That way you can have a unique_ptr_observer that can check if the Threadsafety would still be a problem as you would need either a locking-mechanism, create copies for readout, or some other way to prevent the pointer from getting deleted while you are actively looking at it.
(it is so annoying that the deleter is part of the type.......)
I'm wondering if anyone's run across anything that exists which would fill this need.
Object A contains an object B. It wants to provide access to that B to clients through a pointer (maybe there's the option it could be 0, or maybe the clients need to be copiable and yet hold references...whatever). Clients, lets call them object C, would normally, if we're perfect developers, be written carefully so as to not violate the lifetime semantics of any pointer to B they might have...but we're not perfect, in fact we're pretty dumb half the time.
So what we want is for object C to have a pointer to object B that is not "shared" ownership but that is smart enough to recognize a situation in which the pointer is no longer valid, such as when object A is destroyed or it destroys object B. Accessing this pointer when it's no longer valid would cause an assertion/exception/whatever.
In other words, I wish to share access to data in a safe, clear way but retain the original ownership semantics. Currently, because I've not been able to find any shared pointer in which one of the objects owns it, I've been using shared_ptr in place of having such a thing. But I want clear owneship and shared/weak pointer doesn't really provide that.
Would be nice further if this smart pointer could be attached to member variables and not just hold pointers to dynamically allocated memory regions.
If it doesn't exist I'm going to make it, so I first want to know if someone's already released something out there that does it.
And, BTW, I do realize that things like references and pointers do provide this sort of thing...I'm looking for something smarter.
boost::weak_ptr is what you are looking for. Maybe with some minor tweaks though, like prohibiting creation of shared_ptr from it. Also, this can hold anything, including pointer to memory that is not dynamically allocated.
The semantics you want is similar to Qt's QPointer. This is a pointer that can hold QObjects and nulls itself when the corresponding QObject is deleteed (ordinarily, eg. by operator delete).
However, similar approach has inherent problems - such that the client cannot be sure he isn't using a dangling pointer. eg.
QPointer<T> smart_ptr = original_obj;
T* tmp = smart_ptr; // this might be a function argument etc.
... // later
delete original_obj;
... // even later
tmp->do_something(); // CRASH
This can be avoided using some "hard" references that don't allow object deletion, which is exactly what shared_ptr/weak_ptr do.
BTW, AFAIK, shared_ptr can point to member variables, except it can't manage them. That is, you must provide a custom deleter that doesn't do anything.
I have a class that contains a vector of object pointers. I have a GetObject(..) function in that class that looks through the vector, finds the object desired, and returns a pointer to it. However, if the user of the class does a delete() on that returned pointer, my program will crash because the vector still has a pointer to that, now invalid, object. So, in my GetObject() class, I can return a const pointer, but that doesn't solve the problem because you can still delete the object. The object is mutable so I can't return a pointer to a const object. I suppose I could prevent deletion by returning a reference to the object but I have my function returning NULL if there is an error. I guess I can pass back the object reference via the parameters and then return and error number like this
//-1 on object on found. 0 for success. Object is passed back in
// the function parameter result.
int MyObject::GetObject(int object_id, Item& result)
Is this the best solution for such a situation?
The best way to solve this problem is to use a shared-ownership smart pointer like shared_ptr, which you can find in Boost, C++ TR1, and C++0x.
A smart pointer is a container that manages the lifetime of your dynamically allocated object for you. It takes responsibility for deleteing the object when you are done using it.
With a shared ownership smart pointer, you can have multiple smart pointers that all share ownership of the dynamically allocated object. A reference count is kept that keeps track of how many smart pointers have ownership of the object, and when the last owning smart pointer is destroyed, the dynamically allocated object is deleted.
It is extremely difficult to manage resources manually in C++, and it's very easy to write code that looks correct and works right most of the time but that is still not correct. By using smart pointers and other resource-owning containers (like the standard library containers), you no longer have to manage resource manually. It is significantly easier to write correct code when all of your resource management is automatic.
Automatic resource management in C++ is accomplished using a design pattern called Resource Acquisition is Initialization (RAII), which is arguably the most important design pattern you as a C++ programmer should become familiar with.
Anybody in your code could also try to de-reference NULL. You going to stop them doing that too? If your container owns the object, and you make this clear (returning a raw pointer is usually pretty clear or mention in docs), then anyone who deletes it, the result is their own fault. The only way that you could guarantee the prevention of the deletion of the object is to prevent the user from ever gaining a native reference or pointer - in which case they just can't access the object.
Who are clients of your class? If they are not your mortal enemies you could just ask them nicely not to delete the object. Otherwise, there will always be a way for "them" to mess you up.
Okay, one possible solution is to make destructor private. That will prevent everyone from deleting the object. But then the object has to delete itself (delete this) somehow, maybe through some function called DeletObjectButDontBlameMeIfAppCrashes. If the owner is some other class then you can set the destructor to protected and owner class as friend of this class.
You should return a reference to the object. There are two ways to handle the case when there is no object found.
First, you can use the Null Object Pattern in order to implement a special value for that case. That might not make sense for your case though.
The other way is to split it up into two methods, one which can be used to check if an appropriate element exists, and one to retrieve it.
Many STL-containers or algorithms implement both of these, either by returned a past-the-end iterator, or by having empty() returns false as a prerequisite of calling a method like front or back.
If you can use the boost libraries, smart pointers can be used to ensure that pointers stay valid. Essentially, each reference to a smart pointer increases its "use count" by 1. When the reset function is called on a reference, the reference goes away and the use counter decrements. As long as some object is still holding on to a smart pointer, the reference will be valid. Note that the other classes will be able to change what its pointing to, but can't delete it.
(My description deals mainly with smart pointers specifically - the different types of pointers vary a little, but the general idea remains the same).