In the C++
Inheritance example, we saw how derived classes can inherit from
base classes. This example resulted in code that required
if()
statements to calculate a function for the different
classes. Polymorphism can be used instead to specify different behaviors
for the different classes to avoid conditional statements in the code,
which are hard to maintain and extend.
Set up base class as before. This time, the shared object is the
function, area()
. Note that the area function is proceeded
by virtual and set to equal 0. This is done to specify
this is a function being overwritten by functions from the child
class.
class Shape {
public:
//constructor
Shape() {}
virtual ~Shape() {}
// Virtual function to calculate area
virtual double area() = 0;
};
The child classes are then specified. Again, we use virtual to indicate this function is overwriting the function in the base class.
class Circle : public Shape {
double radius;
public:
Circle(double r) : Shape(){
radius = r;
}
// Override area() for Circle
virtual double area() {
return M_PI * radius * radius;
}
};
class Rectangle : public Shape {
private:
double length, height;
public:
Rectangle(double l, double h) : Shape(){
length = l;
height = h;
}
// Override area() for Rectangle
virtual double area() {
return length * height;
}
};
We are going to expose these functions to R using Rcpp Modules. See Intro to Rcpp for a refresher on Rcpp Modules.
RCPP_MODULE(shape) {
Rcpp::class_<Shape>("Shape")
.method("area", &Shape::area);
Rcpp::class_<Circle>("Circle")
.derives<Shape>("Shape")
.constructor<double>();
Rcpp::class_<Rectangle>("Rectangle")
.derives<Shape>("Shape")
.constructor<double, double>();
}
Now we can load the module using Rcpp::loadModule()
where the second argument is TRUE
so that all objects are
loaded into the R environment. The ‘new’ functions creates a new
instance of the class.
library(Rcpp)
sourceCpp("../src/my_polymorphism.cpp")
loadModule("shape", TRUE)
c <- new(Circle, 2)
r <- new(Rectangle, 3, 4)
c$area()
## [1] 12.56637
r$area()
## [1] 12