6

I'm reading C++ Design Patterns and Derivatives Pricing by Mark Joshi and implementing his code in C++11. Everything has gone pretty well until I hit chapter 4 where he discusses virtual copy constructors.

PayOffDoubleDigital thePayOff(Low, Up);
VanillaOption theOption(thePayOff, Expiry);

The problem here is that VanillaOption contains a reference to thePayOff. If that's the case and someone modifies thePayOff, the the behavior of theOption could be modified unwittingly. The solution he advises is to create a virtual copy constructor in PayOffDoubleDigital's base class, PayOff so that theOption contains its own copy:

virtual PayOff* clone() const = 0;

and then defined in each inherited class:

PayOff* PayOffCall::clone() const
{
    return new PayOffCall(*this);
}

Returning new caught me as something that might be inappropriate in C++11. So what is the proper way of handling this using C++11?

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
BDig
  • 93
  • 7

2 Answers2

13

The solution he advises is to create a virtual copy constructor in PayOffDoubleDigital's base class [...]

First of all, clone() is not a copy-constructor. A copy constructor for class X is a special member function with no return type which usually has the signature:

X(X const&)

And may have the signature:

X(X&)

Function clone() is just a regular (virtual) function, and its particular meaning is recognized by you - the user - as something which creates clones of your object, but not by the compiler, which has no idea what clone() does.

Returning new caught me as something that might be inappropriate in C++11

That's true, using new is not idiomatic in C++11. In fact, in C++11 you should (almost) never use new unless you are doing really low-level memory management (something you should avoid unless you really have to) - and in C++14, you can remove the "almost". Unfortunately, this is likely the exceptional case where new is needed.

I'm saying this because I believe returning a unique_ptr sounds like the appropriate thing to do here (the option object has to hold its own PayOff object, and that must stay alive just as long as the option object is alive), and there is no std::make_unique() function in C++11 (it will be there in C++14):

std::unique_ptr<PayOff> PayOffCall::clone() const
{
    return std::unique_ptr<PayOff>(new PayOffCall(*this));
}

Having VanillaOption (or its base class) hold a unique_ptr rather than a raw pointer would make it unnecessary to delete the PayOff object returned by clone(). In turn, not having to delete that object means no need to define a user-provided destructor, and no need to take care about the Rule of Three, Rule of Five, or whatnot.

Whenever you can, follow R. Martinho's Fernandes's advice and go for the Rule of Zero.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Thank you for the well thought response. This is an area of C++ that has been difficult to master for me. I would mark this up if I could. But, this being my first question, I don't have the necessary reputation. Need more people to mark up the question :) – BDig May 28 '13 at 21:37
  • @BDig: Don't worry, keep asking good questions and you will soon get the rep :) Glad I could help, and good luck with your book – Andy Prowl May 28 '13 at 21:50
1

As often when dealing with ownership, the cleanest solution is to return a smart pointer: it both guarantees exception safety (no risk of memory leak) and makes it clear by whom the objects are owned.

Whether you'll want to use unique_ptr or shared_ptr depends entirely on you.

syam
  • 14,701
  • 3
  • 41
  • 65