Virtual method with default parameter changed behavior

127 Views Asked by At

I was asked to make a slight change on an old project build with Visual Studio 2013 VC++. So, I installed VS2013, made the change, compiled it but its behavior has changed on a code that I didn't touch.

After the update, a virtual method that was calling the method on the derived class is calling the method on the base class now.

I'm not a C++ expert but I can see that this a bad practice. All I want to know if there was a breaking change in C++ around this so I can explain to the person who asked me to update this project.

class BaseItem
{
  public:
    virtual void SetValue(int first, int* second = nullptr)
    {
    };
}

class DerivedItem : public BaseItem
{
  public:
    virtual void SetValue(int first)
    {
    };
}
BaseItem* item = new DerivedItem();
// Before: SetValue on the DerivedItem was called.
// After: SetValue on the BaseItem is called.
item->SetValue(5, nullptr);

and if I edit the DerivedItem like this:

class DerivedItem : public BaseItem
{
  public:
    virtual void SetValue(int first, int* second = nullptr)
    {
    };
}

the SetValue on the DerivedItem is called again.

1

There are 1 best solutions below

1
M.M On BEST ANSWER

Function overriding is based on the parameters and their types, not taking default values into account. After the change, DerivedItem::SetValue is no longer an override of BaseItem::SetValue, it is just a member function that shadows the base function of same name.

You can have the compiler diagnose this for you by using the override keyword whenever you intend overriding to happen:

class DerivedItem : public BaseItem
{
    virtual void SetValue(int first) override

then it will give an error if the function is not actually overriding a function from the base class (as opposed to a silent change in runtime behaviour).


To override a function you will have to make the derived class signature match the base class, as you have found.

In my opinion -- it's cleaner and more robust to avoid default parameters entirely , and add another overload of the function to support a different parameter set. So instead, the base class would have:

virtual void SetValue(int first){ ... }
virtual void SetValue(int first, int second){ ... }

and the derived could override either one or both. This approach also plays nicer with existing code that calls SetValue.