Appendix B. Object Slicing
Another point that was deferred from Chapter 3 is that abstract base classes can prevent object slicing, which will now be explained. In the result from Appendix A, both the Base and Derived classes are concrete; that is, either can be instantiated on its own:
#include <cmath>
#include <iostream>
class Base
{
public:
virtual double trig_fcn(double x) const
{
return std::cos(x);
}
virtual ~Base() = default;
};
class Derived final : public Base
{
public:
double trig_fcn(double x) const override
{
return std::sin(x);
}
};
Suppose now we create instances of each on the stack:
Base b; Derived d;
Suppose also that we have a function that takes in a Base argument and evaluates the trig_fcn(.) member function for and displays the result to the screen:
void slice_function(Base b)
{
using std::cout, std::format;
using namespace std::numbers;
cout << format("slice_function(.): trig_fcn(pi) = {}\n",
b.trig_fcn(pi));
}
Now, call this function with both b and d:
slice_function(b); slice_function(d);
We would get as output something like this:
slice_function(.): trig_fcn(pi) = -1 slice_function(.): trig_fcn(pi) = -1
In both cases, the result is essentially –1 (this might not be exact because of floating-point arithmetic), the result of std::cos(pi), even though we might have expected 0 for std::sin(pi) from the Derived object d.
If we attempt to pass a derived object to a function taking in a base object by value, the code will compile, but the derived object ...