0

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.

9
  • 1
    If the functions do not have the same signature, they are different functions. Read about function overloading in C++. If you want to really see what's going on, declare DerivedItem::SetValue with the override specifier, and only one of your examples will compile.
    – paddy
    Commented Jan 30 at 1:55
  • The DerivedItem::SetValue(int first) is a different function than the BaseItem::SetValue() because they have different parameter lists. To overload functions, they must have the same signature, i.e. quantity of parameters and return type. A default parameter counts as a parameter (the compiler sets the value if the parameter is not specified). Commented Jan 30 at 1:56
  • 1
    Please post a minimal reproducible example. int second = nullptr, missing semicolons are the signs of a not real simplified code.
    – 3CxEZiVlQ
    Commented Jan 30 at 1:57
  • SetValue in DerivedItem does not override SetValue in BaseItem. Never did. What the question describes would be a compiler bug. Commented Jan 30 at 1:58
  • 1
    nullptr is not a valid initializer for an int
    – M.M
    Commented Jan 30 at 2:00

1 Answer 1

2

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.

1
  • Since it seems that there were not any breaking changes around this, I might have a different version of the project. I'll follow your tips and fix the code, so the app behaves the way it's supposed to. Thanks! Commented Jan 30 at 2:14

Not the answer you're looking for? Browse other questions tagged or ask your own question.