0

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.

2
  • from a cooking stand point, there is no reason for the pie to store the fruit as reference, as it take ownership on it. Commented Nov 16, 2019 at 8:50
  • not related: consider using a different name for member variale and parameters: this fruit(fruit) is so unreadable Commented Nov 16, 2019 at 9:36

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

6 Comments

I am trying the solution with std::unique_ptr but it doesn't seem to work. lemon_ is correctly initialized, but fruit_ isn't. Hovering over fruit_ in the debugger says <struct at NULL>.
@NikolaosGeorgiou, I don't see any problem: godbolt.org/z/vt8Dhv Note the order of initialization: lemon_, then fruit_.
Wow great site, I didn't know about it. Unfortunately, I can reproduce my problem also on that site with your code. I added the Slice method. When I call pie.lemon_->Slice() it works. When I call pie.fruit_.Slice() it crashes. godbolt.org/z/fbZsVQ
@NikolaosGeorgiou, I fixed the error. lemon_ should precede fruit_ to be initialized before it.
|
1

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.

2 Comments

I still need both constructors of Pie. The one that accepts the reference to the abstract Fruit class and the one that accepts the lemon size. How will the two constructors look like in that case?
You can keep both constructors. You just need to provide storage for the Lemon object that the int size overload creates.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.