Playing around with C++ after a longer break.
I have a std::vector of unique pointers to objects through which I iterate, calling a function that takes as argument the base class from which the objects in the vector are derived, like so:
for (auto const& object : objects)
{
_process(object);
}
where objects would be:
std::vector<std::unique_ptr<Derived>>
I would like to understand how would I enforce polymorphic behavior to call the function:
_process(std::unique_ptr<Base> base)
std::unique_ptr represents ownership, and therefore can not be copied.
That means that _process(std::unique_ptr<Base> base) is a sink, i.e. it consumes the object (it can destroy it or store it somewhere, but the caller has no control over it anymore). In order to call it you'd have to use std::move:
_process(std::move(object))
that way you explicitly reject the ownership over the object and essentially remove it from your container - the pointer will be reset to nullptr.
Naturally that can only be done via non-const reference.
If you don't want to transfer the ownership and just want to do something with the object - which the name _process seem to imply - you're supposed to pass the regular raw pointer:
_process(Base* base);
...
for (auto const& object : objects)
{
_process(object.get());
}
Raw pointers are still great for everything except controlling the lifetime of your objects.
In both cases (raw pointer and unique_ptr) there should be absolutely no problem with implicitly converting from Derived to Base.
Related
What would the best practice to hold a non owning reference to a object, that can be deleted?
The first part is fairly simple, I simply using the stupid-smart pointer: observer_ptr. However, the last part makes it somewhat more difficult.
Example
Having this setup, to illustrate the need of my vector unique ptr
class Object
{
};
class Derrived : public Object
{
};
With the implementation of
vector<nonstd::observer_ptr<Object>> _observers;
vector<unique_ptr<Object>> _objects;
auto t = make_unique<Derrived>();
_observers.push_back(nonstd::make_observer(t.get()));
_objects.push_back(move(t));
// Same objects
cout << (_observers.at(0).get() == _objects.at(0).get()) << endl;
Issue
Now at any time, somewhere, one of the objects in _objects might be deleted.
I will simply illustrate this by deleting the first object in the vector:
_objects.erase(_objects.begin());
This will result in the _objects vector is empty. However, the _observers vector now points to a freed memory space.
Of course, I can simply delete the observer from _observers, but imagine having such observing references in different parts of my program.
Would there be any cleaner solution for this, and it this the right way to observe different objects?
Please let me know if the example at hand does not illustrate the problem (or any problem for that matter) that I described.
Your use-case sounds like a std::weak_ptr<Object> would be suitable non-owning representation. Of course, for a std::weak_ptr<T> the owning representation is std::shared_ptr<T>. However, since you’ll need to “pin” the object before you could access a std::weak_ptr<T> you’d have more than one owner anyway while accessing the pointer.
As stated in the comments, this is a typical use-case for std::weak_ptr:
std::weak_ptr is a smart pointer that holds a non-owning ("weak")
reference to an object that is managed by std::shared_ptr. It must be
converted to std::shared_ptr in order to access the referenced object.
Example:
vector<shared_ptr<Object>> objects;
objects.push_back(make_shared<Derived>());
weak_ptr<Object> ptr{ objects.back() };
auto sh_ptr = ptr.lock(); // increase reference count if object is still alive
if(sh_ptr) { // if object was not deleted yet
sh_ptr->doStuff(); // safely access the object, as this thread holds a valid reference
}
Today there is no way to make non-owning relationship to be enforced by compiler:
1. weak_ptr could be converted to shared_ptr
2. Everything else could be deleted.
3. Wrappers around weak_ptr that would be non convertible to shared_ptr would not work also: once reference to an object is retrieved it could be deleted too.
I'd like to optimize my code. I have one class that has a shared_ptr data member. In some methods of this class, I create objects that need to use this member (just to get information from the object pointed by shared_ptr). I know that lifetime of these created objects is lower than in my main class.
How to pass this pointer? I think another shared_ptrs is unnecessary (because I have a warranty that the object will exist). So what should get my created classes? Should they get raw pointer? Weak_ptr? Or the best solution is getting shared_ptr (and incrementing its reference counter)? What is the most standard solution?
In this case when you know the life-time of your shared resource will outlive those that you pass the pointer to the correct thing to do is pass a reference or a raw pointer:
void func(object* o)
{
// do stuff with o
}
// ...
std::shared_ptr<object> sp;
// ...
func(sp.get()); // pass raw pointer
The main reason for this is that the function can be useful no matter what kind of smart pointer is managing the resource. By accepting the raw pointer your function is able to accept objects from shared pointers as well as unique pointers and any other third party smart pointer.
There is no benefit to passing in the smart pointer unless the function needs to modify the smart pointer itself.
A good set of guidelines being produced by Bjarne Straustrup & Herb Sutter can be found here: CppCoreGuidelines
The rule about passing raw pointers (or references):
F.7
Passing a smart pointer transfers or shares ownership and should only be used when ownership semantics are intended. A function that does not manipulate lifetime should take raw pointers or references instead.
Passing by smart pointer restricts the use of a function to callers that use smart pointers. A function that needs a widget should be able to accept any widget object, not just ones whose lifetimes are managed by a particular kind of smart pointer.
When passing the shared_ptr into a function that will not store the resource, pass it by reference:
void foo(const shared_ptr<T>& ptr)
{
// Basically just a pointer dereference
std::cout << ptr->bar() << '\n';
}
int main()
{
std::shared_ptr<T> ptr{std::make_shared<T>()};
foo(ptr);
}
That won't increment the reference count, but that's fine — you're effectively treating it as a raw pointer (because you're just temporarily inspecting the pointee) but in a way that's safe because if you accidentally copy it then you'll get the reference count increment that can save your life. :)
However, if foo needs to store any sort of handle to this object, then you should pass in the shared_ptr by copy … or consider using weak_ptr so that you at least get some semblance of safety.
The above contrived example is so simple that I'd actually make it the following:
void foo(const T& ptr)
{
std::cout << ptr.bar() << '\n';
}
int main()
{
std::shared_ptr<T> ptr{std::make_shared<T>()};
foo(*ptr.get());
}
I have a C++ project where I store objects in cells in a grid container. Every cell may contain one or zero objects stored in a std::unique_ptr. Naturally all methods having these objects as arguments should take a const std::unique_ptr reference to maintain the "uniqueness".
Secondly, when something happens to the objects they emit a signal with themselves as one of the arguments; these signals are caught by the grid container in a single handler (therefore we need the object reference). The handler may take some action on the object or emit its own signal, passing the object reference further.
The problem is that the objects themselves obviously cannot return a std::unique_ptr reference to themselves, while all other methods operating on them expects one. Is there a way to solve this, or do I have to let go of unique pointers and use raw ones?
Here's a code example using the sigc++ library (please allow for minor errors since I haven't tested it):
class Inhabitant
{
public:
void sos()
{
signal_distress.emit (*this);
}
// Signals
sigc::signal<void, Inhabitant &> signal_distress;
};
class Cell
{
public:
std::unique_ptr<Inhabitant> set_inhabitant (std::unique_ptr<Inhabitant> inhabitant)
{
// Set new inhabitant, return previous one...
}
private:
std::unique_ptr<Inhabitant> m_inhabitant;
};
class Grid
{
public:
void add_inhabitant_at (std::unique_ptr<Inhabitant> inhabitant,
unsigned int x, unsigned int y)
{
// Connect the inhabitant to our rescue team
inhabitant->signal_distress.connect (sigc::mem_fun (*this,
&Grid::on_inhabitant_distress));
// Place in cell
m_cells[x][y].set_inhabitant (std::move (inhabitant));
}
private:
// Data
Cell m_cells[100][100];
// Helpers
void help_inhabitant (const std::unique_ptr<Inhabitant> &inhabitant)
{
// Do something helpful
}
// Signal handlers
void on_inhabitant_distress (Inhabitant &inhabitant)
{
// Now, how do I call help_inhabitant(), or any other function that expects
// a unique_ptr reference?
}
};
It is a best practice not to pass smart pointers such as unique_ptr to functions that do not need to take (or share) ownership of the resource managed by the unique_ptr. Put another way, I don't think you would ever want to pass a unique_ptr by const reference. Instead, all the functions that are taking these unique_ptr references really only need to take a const Inhabitant &. For example:
void help_inhabitant (const Inhabitant &inhabitant) {
// do stuff with the inhabitant directly
}
Naturally all methods having these objects as arguments should take a const std::unique_ptr reference to maintain the "uniqueness".
No, the object still has a single unique owner, no matter how many other pieces of code can access it via non-owning pointers or references. Your idea that passing around const unique_ptr<T>& maintains any kind of invariant or enforces a policy is an illusion.
Is there a way to solve this, or do I have to let go of unique pointers and use raw ones?
You don't have to give it up competely, just where it's inappropriate. Use unique_ptr for managing ownership and lifetime, use raw pointers for simply referring to an object that is managed by some other piece of code.
surely the whole point about unique_ptr is that you pass references to it, rather than a raw pointer?
No, definitely not.
The unique_ptr manages the ownership of the object, but not doesn't have to be used for access to the object.
If you want to refer to the object without owning it then passing references or pointers to the object is fine (as long as the code receiving those pointers or references doesn't think it is taking ownership and try to delete the object). The code that just wants to use Inhabitant doesn't need to care that it is owned by a unique_ptr, it just wants to use the object. How its lifetime is managed is someone else's concern, and the code that doesn't own the object should not be made dependent on the ownership policy. Avoiding that dependency would allow you to change the owner to use shared_ptr or some other mechanism, and the signal handlers would be unaffected because they do not have to change how they access the object.
Pass a unique_ptr by value (or rvalue reference) to transfer ownership. Do not pass a unique_ptr by const-reference, because it's completely useless, the caller can't do anything with it that can't be done with a raw pointer.
Using a reference to a unique_ptr actually introduces a new class of bug that wouldn't exist otherwise:
void register_callback(func_type f, const unique_ptr<T>& obj);
unique_ptr<T> p(new T);
register_callback(func, p); // stores reference to p
unique_ptr<T> p2 = std::move(p);
Now the signal handler still refers to p which is going to be empty when the callback happens. The identity of the unique_ptr is completely irrelevant, all that matters is that exactly one unique_ptr object owns the pointer, it doesn't matter which one owns it. But you have made the callback depend on the exact instance of unique_ptr by binding a reference to it, so you cannot move that (so you can never move a Cell, which means you can't store it in a container such as vector that might reallocate and move its elements)
If you do it this way instead the callback refers to the object, and it doesn't matter precisely where it's stored:
void register_callback(func_type f, T* obj);
unique_ptr<T> p(new T);
register_callback(func, p.get()); // stores p.get()
unique_ptr<T> p2 = std::move(p);
The callback's copy of the p.get() pointer remains valid even though ownership of it transfers from one object to another.
OK, solved the problem when I realized there is no reason why the cell inhabitants should send a reference to themselves with the signal. Instead the listener (the grid) can bind a reference to the inhabitant (the unique_ptr to it, that is) when registering with the signal:
inhabitant->signal_distress().connect (std::bind (&Grid::on_inhabitant_distress,
this,
std::cref (inhabitant));
This way the signal handler can take a unique_ptr:
void on_inhabitant_distress (const std::unique_ptr<Inhabitant> &inhabitant)
{
// Now everything is fine!
help_inhabitant (inhabitant);
}
and the "uniqueness" chain stays intact.
I have some code that currently uses raw pointers, and I want to change to smart pointers. This helps cleanup the code in various ways. Anyway, I have factory methods that return objects and its the caller's responsibility to manager them. Ownership isn't shared and so I figure unique_ptr would be suitable. The objects I return generally all derive from a single base class, Object.
For example,
class Object { ... };
class Number : public Object { ... };
class String : public Object { ... };
std::unique_ptr<Number> State::NewNumber(double value)
{
return std::unique_ptr<Number>(new Number(this, value));
}
std::unique_ptr<String> State::NewString(const char* value)
{
return std::unique_ptr<String>(new String(this, value));
}
The objects returned quite often need to be passed to another function, which operates on objects of type Object (the base class). Without any smart pointers the code is like this.
void Push(const Object* object) { ... } // push simply pushes the value contained by object onto a stack, which makes a copy of the value
Number* number = NewNumber(5);
Push(number);
When converting this code to use unique_ptrs I've run into issues with polymorphism. Initially I decided to simply change the definition of Push to use unique_ptrs too, but this generates compile errors when trying to use derived types. I could allocate objects as the base type, like
std::unique_ptr<Object> number = NewNumber(5);
and pass those to Push - which of course works. However I often need to call methods on the derived type. In the end I decided to make Push operate on a pointer to the object stored by the unique_ptr.
void Push(const Object* object) { ... }
std::unique_ptr<Object> number = NewNumber(5);
Push(number.get());
Now, to the reason for posting. I'm wanting to know if this is the normal way to solve the problem I had? Is it better to have Push operate on the unique_ptr vs the object itself? If so how does one solve the polymorphism issues? I would assume that simply casting the ptrs wouldn't work. Is it common to need to get the underlying pointer from a smart pointer?
Thanks, sorry if the question isn't clear (just let me know).
edit: I think my Push function was a bit ambiguous. It makes a copy of the underlying value and doesn't actually modify, nor store, the input object.
Initially I decided to simply change the definition of Push to use
unique_ptrs too, but this generates compile errors when trying to use
derived types.
You likely did not correctly deal with uniqueness.
void push(std::unique_ptr<int>);
int main() {
std::unique_ptr<int> i;
push(i); // Illegal: tries to copy i.
}
If this compiled, it would trivially break the invariant of unique_ptr, that only one unique_ptr owns an object, because both i and the local argument in push would own that int, so it is illegal. unique_ptr is move only, it's not copyable. It has nothing to do with derived to base conversion, which unique_ptr handles completely correctly.
If push owns the object, then use std::move to move it there. If it doesn't, then use a raw pointer or reference, because that's what you use for a non-owning alias.
Well, if your functions operate on the (pointed to) object itself and don't need its address, neither take any ownership, and, as I guess, always need a valid object (fail when passed a nullptr), why do they take pointers at all?
Do it properly and make them take references:
void Push(const Object& object) { ... }
Then the calling code looks exactly the same for raw and smart pointers:
auto number = NewNumber(5);
Push(*number);
EDIT: But of course no matter if using references or pointers, don't make Push take a std::unique_ptr if it doesn't take ownership of the passed object (which would make it steal the ownership from the passed pointer). Or in general don't use owning pointers when the pointed to object is not to be owned, std::shared_ptr isn't anything different in this regard and is as worse a choice as a std::unique_ptr for Push's parameter if there is no ownership to be taken by Push.
If Push does not take owenrship, it should probably take reference instead of pointer. And most probably a const one. So you'll have
Push(*number);
Now that's obviously only valid if Push isn't going to keep the pointer anywhere past it's return. If it does I suspect you should try to rethink the ownership first.
Here's a polymorphism example using unique pointer:
vector<unique_ptr<ICreature>> creatures;
creatures.emplace_back(new Human);
creatures.emplace_back(new Fish);
unique_ptr<vector<string>> pLog(new vector<string>());
for each (auto& creature in creatures)
{
auto state = creature->Move(*pLog);
}
I have a class that is responsible for creating and initializing a number of large objects, as the objects are all of the same Type and I don't want to repeat the same initializing code for all the objects, I call an Init method for each object, for example:
InitObject(objMember);
void Test::InitObject(LargeObject * obj)
{
obj = new LargeObject;
obj->Load();
obj->SetSomeProperty(false);
}
Once this has been done, from a public method I call a set of methods to get a pointer to each of the objects:
//public
LargeObject * Test::GetObject()
{
return objMember;
}
The issue is that the objects are losing scope, when InitObject is called, the objects are correctly constructed and populated, but when I call GetObject, it has lost everything.
I'm probably missing something trivial, but I can't see why it's going out of scope.
It is trivial, yes. You're initializing a copy of the original pointer. You probably want to pass it by reference:
void Test::InitObject(LargeObject*& obj)
Passing by value means that you're assigning the return of new to a copy of the pointer. The one outside the function InitObject remains unchanged.
A few more things - initializing objects after construction should be done with care. If the object isn't valid after construction, it's a bad design (excluding some rare cases). You can signal invalid initialization by throwing an exception from the constructor.
Also, consider using smart pointers instead of raw pointers.