Yes, pure pointers are just enough. But objects that have a handle (pointer) to the Thing
have to assume that the object being pointed to will outlive them.
C++11 should raw pointers ever be used when there is one owner (std::unique_ptr) but other objects need a "handle" to the object?
Question
I seem to find quite a lot of code in the following situation:
class Thing
{
public:
Thing() = default;
};
class Repo
{
public:
Repo()
{
// Makes things..
mThings.emplace_back( std::make_unique<Thing>() );
}
// I find I need functions like this,
// a function which may return some record,
// or might return nullptr if there is no record.
Thing* GetThing(int id)
{
// Might return nullptr, or might return
return mThings[0].get();
}
private:
std::vector<std::unique_ptr<Thing>> mThings;
};
The object which uses Repo
to grab a Thing
doesn't own it, so if it OK/acceptable to use a raw pointer in this situation?
It seems like wrong to enforce it to be a std::shared_ptr
and return a std::weak_ptr
as the caller knows that if GetThing
doesn't return nullptr
then the object will live as long as it does. Plus the ownership isn't actually shared.
class SomeObj
{
public:
SomeObj(Repo& repo, int id)
: mRepo(repo), mMyThing(nullptr), mId(id)
{
mMyThing = mRepo.GetThing(mId);
}
private:
Repo& mRepo;
Thing* mMyThing;
int mId;
};
Solution
OTHER TIPS
There is a working draft for std::optional
and a boost library, which would do something similar to what you need:
// make sure to include <boost/optional/optional.hpp>
boost::optional<Thing&> GetThing(int id)
{
// Might return nullptr, or might return
return boost::optional<Thing&>(*mThings[0]);
}
The boost::optional
class is supposed to cover cases like yours, where an object might be returned, but sometimes it will not. This has been covered in the past by using std::pair<bool, T>
or using a pointer, if the object to be returned was a reference.
Using boost::optional
makes semantics more clear, because you
- state that you won’t always return an object
- make clear, by returning a reference, that the receiver does not take ownership
The usage would be something like this:
Repo someRepo;
boost::optional<Thing&> someThing = someRepo;
if (someThing) {
// use someThing just like a pointer
} else {
// do not touch the value
}
(it might be possible to use someThing
like a pointer directly; I have not tested this, I built this from the documentation)
Note: I’m not entirely sure whether the upcoming std::optional
will support references. boost::optional
does support them. Using a reference is kinda the point here though, because as the OP already noted in a comment on the question, having the bare type here would lead to copying, and using a raw pointer as T would kinda defeat the purpose.
@JohannesD commented a link to World’s dumbest smart pointer, which is made exactly for this purpose. I have no information about the status of this draft though.
However: Nowadays, I still tend to use raw pointers in that case, mainly because std::optional
hasn’t hit implementations (and isn’t even fully specified and might not support references at all) yet. I am also a bit disappointed that it has been removed from C++14.
Make sure to make it clear in the documentation of that function that the receiver does not take ownership.
IMO, it's acceptable/OK as long as you NEVER want to use the code with multiple threads. If you might want to add multithreading in the future, it's a real problem, as the caller has no way of knowing how long the returned pointer will be valid, as another thread might do something that invalidates the pointer. In such cases a std::shared_ptr
makes more sense (both in the data structure and as the return value).