Dynamic Dispatch: Virtual Methods¶
Simple Is-A Relationship Is Not Enough¶
If
Sensor* s
actually points to aMySensor
object, why isn’tMySensor::get_value()
called?If
MySensor
is-aSensor
, how do I use it as-aSensor
?In other words: how to I use a
Sensor*
to callMySensor::get_value()
Enter virtual
¶
Use
virtual
on those methods that need dynamic dispatchThrough the
Sensor*
(which points to an object of derived typeMySensor
), the actual dynamic type is determined⟶ Run Time Type Information (RTTI)
Question: why isn’t that the default?
Answer: performance ⟶ indirect function call cannot be default
#include <iostream>
class Sensor
{
public:
Sensor(double value) : _value{value} {}
virtual double get_value() const // <-- virtual: dynamic dispatch
{
std::cout << "Sensor::get_value(): " << _value << std::endl;
return _value;
}
private:
double _value;
};
class MySensor : public Sensor
{
public:
MySensor(double basetemp, double correction)
: Sensor{basetemp},
_correction{correction} {}
double get_value() const
{
double value = Sensor::get_value() + _correction;
std::cout << "MySensor::get_value(): " << value << std::endl;
return value;
}
private:
double _correction;
};
Virtual Methods: Effects¶
Note how
MySensor::get_value()
is called through the base pointer
#include "sensors.h"
#include <iostream>
int main()
{
MySensor ms{37.3, 0.25};
Sensor* ps = &ms;
double value = ps->get_value(); // <-- dynamic dispatch to MySensor::get_value()
std::cout << value << std::endl;
return 0;
}
$ ./code/virtual-rudimentary/cxx-inher-oo-virtual-rudimentary
Sensor::get_value(): 37.3
MySensor::get_value(): 37.55
37.55
Virtual Methods: Where Not To Place virtual
¶
C++ lets you complete freedom
virtual
is just a mechanism to achieve dynamic dispatch⟶ no policy!
If you want to use
Base*
polymorphically, this is the place wherevirtual
is written
Attention
virtual
has to come at dynamic dispatch entry point!
#include <iostream>
class Sensor
{
public:
Sensor(double value) : _value{value} {}
double get_value() const // <-- not a virtual method
{
std::cout << "Sensor::get_value(): " << _value << std::endl;
return _value;
}
private:
double _value;
};
class MySensor : public Sensor
{
public:
MySensor(double basetemp, double correction)
: Sensor{basetemp},
_correction{correction} {}
virtual double get_value() const // <-- virtual, but too late down the hierarchy
{
double value = Sensor::get_value() + _correction;
std::cout << "MySensor::get_value(): " << value << std::endl;
return value;
}
private:
double _correction;
};
#include "sensors.h"
#include <iostream>
int main()
{
MySensor ms{37.3, 0.25};
Sensor* ps = &ms;
double value = ps->get_value(); // <-- **not** dynamically dispatched
std::cout << value << std::endl;
return 0;
}
$ ./code/virtual-wrong/cxx-inher-oo-virtual-wrong
Sensor::get_value(): 37.3
37.3
override
: Problem¶
Common mistake in pre C++11: misspelled method signatures
E.g.
double get_value() const
anddouble get_value()
are different methods⟶ Base method not overridden!
#include <iostream>
class Sensor
{
public:
Sensor(double value) : _value{value} {}
virtual double get_value() const // <-- virtual: dynamic dispatch
{
std::cout << "Sensor::get_value(): " << _value << std::endl;
return _value;
}
private:
double _value;
};
class MySensor : public Sensor
{
public:
MySensor(double basetemp, double correction)
: Sensor{basetemp},
_correction{correction} {}
double get_value() // <-- "const" omitted -> different method
{
double value = Sensor::get_value() + _correction;
std::cout << "MySensor::get_value(): " << value << std::endl;
return value;
}
private:
double _correction;
};
#include "sensors.h"
#include <iostream>
int main()
{
MySensor ms{37.3, 0.25};
Sensor* ps = &ms;
double value = ps->get_value(); // <-- *not* overridden by MySensor
std::cout << value << std::endl;
return 0;
}
$ ./code/virtual-override-problem/cxx-inher-oo-virtual-override-problem
Sensor::get_value(): 37.3
37.3
override
: Solution¶
override
, placed at the overriding method in the derived classesCompiler then checks if something is overridden
Rules
virtual
at the topmost definition (dynamic dispatch entry point)override
at every method that overrides/re-implements the base method
Note that
override
impliesvirtual
, sovirtual
is not specified together withoverride
#include <iostream>
class Sensor
{
public:
Sensor(double value) : _value{value} {}
virtual double get_value() const // <-- virtual (topmost)
{
std::cout << "Sensor::get_value(): " << _value << std::endl;
return _value;
}
private:
double _value;
};
class MySensor : public Sensor
{
public:
MySensor(double basetemp, double correction)
: Sensor{basetemp},
_correction{correction} {}
double get_value() const override // <-- overriding method
{
double value = Sensor::get_value() + _correction;
std::cout << "MySensor::get_value(): " << value << std::endl;
return value;
}
private:
double _correction;
};
#include "sensors.h"
#include <iostream>
int main()
{
MySensor ms{37.3, 0.25};
Sensor* ps = &ms;
double value = ps->get_value();
std::cout << value << std::endl;
return 0;
}
$ ./code/virtual-override-solution/cxx-inher-oo-virtual-override-solution
Sensor::get_value(): 37.3
MySensor::get_value(): 37.55
37.55