Dynamic Dispatch: Virtual Methods#
Simple Is-A Relationship Is Not Enough#
If
Sensor* sactually points to aMySensorobject, why isn’tMySensor::get_value()called?If
MySensoris-aSensor, how do I use it as-aSensor?In other words: how to I use a
Sensor*to callMySensor::get_value()
Enter virtual#
Use
virtualon 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
virtualis just a mechanism to achieve dynamic dispatch⟶ no policy!
If you want to use
Base*polymorphically, this is the place wherevirtualis written
Attention
virtualhas 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() constanddouble 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
virtualat the topmost definition (dynamic dispatch entry point)overrideat every method that overrides/re-implements the base method
Note that
overrideimpliesvirtual, sovirtualis 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