As I make the transition from C# to C++ I get a lot of recommendations to use value semantics where possible. It's pretty much guaranteed that if I post a question with a pointer anywhere someone will come along and suggest that it should be a value instead. I'm starting to see the light and I have found a lot of places in my code where I could replace dynamic allocation and pointers with stack allocated variables (and usually references). So I think I have a grasp on using stack allocated objects and passing them to other functions as references when the object lifetime is longer in the caller than the callee.
However I have a question about passing objects by value when the callee will take ownership. Take the following example:
class Zoo
{
void AddAnimal(Animal animal);
std::list<Animal> animals_;
}
Typically from a flexibility and unit testing perspective I'd want Animal to be an interface (abstract class in C++) so I can easily send arbitrary animals and mock it out with a mock implementation.
In a pointer implementation client code would be calling this like:
Animal animal = new Lion("Bob");
myZoo.AddAnimal(animal);
Here the client code doesn't really need the animal object. It's just constructing it temporarily to pass to the method. So in this case there aren't shared semantics. So it seems like a good case for value semantics. However, my understanding is that you can't use Animal as a parameter passed by value because it's an abstract class.
Most of my member functions that don't take primitive types take abstract class parameters. So what is the C++ method to handle this problem? (That is how do you program to interfaces in C++ with value semantics?)
The typical solution for your scenario would involve a resource-managing handler object which you do pass by value. Popular candidates are shared_ptr
and unique_ptr
:
#include <list>
#include <memory>
#include "all_zoo_animals.h" // yours
typedef std::shared_ptr<Animal> AnimalPtr; // see note
typedef std::list<AnimalPtr> AnimalCollection;
AnimalCollection zoo;
void addAnimal(AnimalPtr a)
{
zoo.push_back(a);
}
int main()
{
AnimalPtr a = AnimalPtr(new Penguin);
a.feed(fish);
addAnimal(a); // from local variable, see note
addAnimal(AnimalPtr(new Puffin)); // from temporary
}
If it is feasible, you could also define AnimalPtr
as std::unique_ptr<Animal>
, but then you have to say addAnimal(std::move(a));
. This is more restrictive (as only one object handles the animal at any given time), but also lighter-weight.