Reputation: 2874
Consider the following C++ classes:
// An abstract fruit that can be sliced
class AbstractFruit
{
public:
virtual void Slice() = 0;
};
// A lemon (concrete implementation of AbstractFruit)
class Lemon : public AbstractFruit
{
int size; // small medium large
public:
Lemon(int size) : size(size) {}
virtual void Slice() override { /* slice it */ }
};
// A pie uses a fruit reference
class Pie
{
AbstractFruit& fruit;
public:
Pie(AbstractFruit& fruit) : fruit(fruit) {}
};
This all works fine.
For backwards compatibility reasons, the Pie API needs to have a constructor that creates a lemon pie:
Pie(int size) : fruit(Lemon(size)) {}
This doesn't work. To the best of my understanding, I'm creating a new Lemon instance in the parameter list of the constructor and initializing the fruit reference with it. But, the Lemon instance gets destructed immediately after that, leaving the fruit reference pointing to a destroyed object.
What's the best way to make this work?
I understand I could switch the Pie to use a pointer instead of a reference, but I'd like to use a reference if possible.
Upvotes: 0
Views: 68
Reputation: 26362
If the size of Pie
object is not critical, one possible solution is to let Pie
manage optional Lemon
:
class Pie
{
std::optional<Lemon> lemon_;
AbstractFruit& fruit_;
public:
Pie(AbstractFruit& fruit) : fruit_(fruit) {}
Pie(int size) : lemon_(size), fruit_(*lemon_) {}
};
std::optional
is optional, std::unique_ptr
can be used instead:
class Pie
{
std::unique_ptr<Lemon> lemon_;
AbstractFruit& fruit_;
public:
Pie(AbstractFruit& fruit) : fruit_(fruit) {}
Pie(int size) : lemon_(std::make_unique<Lemon>(size)), fruit_(*lemon_) {}
};
In the first case, Lemon
will be stored inside Pie
itself, in the second case, Lemon
will be allocated on heap, and Pie
will store only a pointer to it. If Lemon
size is small (<= sizeof(void*)
), std::optional
is a better option.
Note that the members are initialized in order of declaration in the class, that's why lemon_
declaration should come before fruit_
one.
Upvotes: 3
Reputation: 1852
A reference is just a memory address, either 32-bit or 64-bit. Where should the actual bytes of your Lemon
live after you have finished calling the constructor?
Currently they live in the stack, which is cleaned up after you finish calling the constructor. To avoid this you need to provide some other storage location for your Lemon
which lives at least as long as your Pie
.
The easiest way to do this is to use new
to allocate Lemon
on the heap, and then decide when you are going to clean it up later.
Upvotes: 1