Search code examples
c++overriding

How to override a method of a base class which is an input of a function?


I am trying to override a method in a code I am using to learn about this but I didn't succeed, what am I missing?

Here some code to reproduce my issue:

my_class.h

#ifndef MY_CLASS_H
#define MY_CLASS_H


class Base {
    public:
        Base();
        virtual ~Base();
        virtual double evaluate(double x);

};

double call_evaluate(Base funct, double x);

#endif

my_class.cpp

Base::Base () {}
Base::~Base () {}
double Base::evaluate(double x){
        std::cout << "evaluate base\n";
    return 0.0;
};

double call_evaluate(Base funct, double x){
    return funct.evaluate(x);
}

main.cpp

#include "my_class.h"
#include "iostream"


class Derived: public Base{
private:
    double value;

public:

    Derived(double value){
        this->value = value;
    }

    double evaluate(double x) override{
        std::cout << "evaluate derived\n";
        return this->value*x;
    }

};


int main(){

    Derived problem(6);
    double a=problem.evaluate(2);
    double b=call_evaluate(problem, 2);
    std::cout << "a = " << a << ", expected = 12\n";
    std::cout << "b = " << b << ", expected = 12\n";

    return 0;
}

Output

evaluate derived
evaluate base
a = 12, expected = 12
b = 0, expected = 12

Solution

  • In short, you need to change call_evaluate to receive the first argument by reference.

    double call_evaluate(Base& funct, double x);
    

    For the reason why your program behaves like it does, see below.

    Your coding style suggests you come from a Java-like background. You must make yourself aware of a very important point about C++. In the function declared below, both arguments are passed by value, not by reference.

    double call_evaluate(Base funct, double x);
    

    "by value" in current C++ parlance means the source object is going to be copied, that is, an unnamed copy of the original object is going to be created then passed to the function. "by reference" means the source object is merely going to be referenced or aliased, that is, the function receives a reference or alias to the same object.

    From a Java-like programmers perspective, this should look like the pseudo-code below.

    auto tmp0 = funct.clone();
    auto tmp1 = x.clone();
    call_evaluate(tmp0, tmp1);
    

    In C++, you may achieve the effect of "by reference" by explicitly marking the parameter type as a reference type with the & declarator.

    double call_evaluate(Base& funct, double x);
    

    Doing "by reference" with reference types require no special notation on the callers side.

    call_evaluate(func, x);
    

    Additionally, in C and C++, you may achieve the effect of "by reference" by passing object addresses as parameters. You do this by explicitly marking the parameter type as a pointer type with the * declarator.

    double call_evaluate(Base* funct, double x);
    

    Calling "by reference" with a pointer type requires the caller to use the unary & operator to obtain the object's address.

    call_evaluate(&func, x);
    

    Now, what is the problem with your program? You are "slicing" a polimorphic object.

    Since call_evaluate receives the first parameter "by value", C++ will generate a "copy" expression to create a new, temporary, unnamed object from the source object.

    The target type for this "copy" expression will be the type of the parameter, Base. In other words, C++ will generate an expression for the purpose of creating a new, temporary, unnamed object of the Base type from the source object.

    Since this temporary, unnamed object has type Base, you will readily see how it polimorphically behaves just like any Base type should.

    This is called "slicing" since it "slices" the object, that is, it creates a new object from just the Base part of the source object.

    You may benefit from doing these experiments:

    Declare call_evaluate as a pure virtual function. If there is no definition in the base class, what will this program do? For more information, see https://en.cppreference.com/w/cpp/language/abstract_class

    Forbid Base copy constructor and copy assignment operator with the = delete special syntax. If copying Base is not allowed, what will the compiler do? For more information, see https://en.cppreference.com/w/cpp/language/function#Deleted_functions