Casting: static_cast versus dynamic_cast in C++

The dynamic_cast operator can be used to cast an object to its actual type at runtime.

Suppose you wish to rewrite the displayGeometricObject function in Listing 15.19, TestAbstractGeometricObject.cpp, to display the radius and diameter for a circle object and the width and height for a rectangle object. You may attempt to implement the function as follows:

void displayGeometricObject(GeometricObject& g)

{

cout << “The raidus is ” << g.getRadius() << endl;

cout << “The diameter is ” << g.getDiameter() << endl;

cout << “The width is ” << g.getWidth() << endl;

cout << “The height is ” << g.getHeight() << endl;

cout << “The area is ” << g.getArea() << endl;

cout << “The perimeter is ” << g.getPerimeter() << endl;

}

There are two problems with this code. First, the code cannot be compiled because g’s type is GeometricObject, but the GeometricObject class does not contain the getRadius(), getDiameter(), getWidth(), and getHeight() functions. Second, your code should detect whether the geometric object is a circle or a rectangle and then display radius and diameter for a circle and width and height for a rectangle.

The problems may be fixed by casting g into Circle or Rectangle, as shown in the following code:

void displayGeometricObject(GeometricObject& g)

{

GeometricObject* p = &g;

cout << “The raidus is ” <<

static_cast<Circle*>(p)->getRadius() << endl;

cout << “The diameter is ” <<

static_cast<Circle*>(p)->getDiameter() << endl;

cout << “The width is ” <<

static_cast<Rectangle*>(p)->getWidth() << endl;

cout << “The height is ” <<

static_cast<Rectangle*>(p)->getHeight() << endl;

cout << “The area is ” << g.getArea() << endl;

cout << “The perimeter is ” << g.getPerimeter() << endl;

}

Static casting is performed on p that points to a GeometricObject g (line 3). This new function can compile but is still incorrect. A Circle object may be cast to Rectangle to invoke getWidth() in line 10. Likewise, a Rectangle object may be cast to Circle to invoke getRadius() in line 5. We need to ensure that the object is indeed a Circle object before invoking getRadius(). This can be done using dynamic_cast.

The dynamic_cast works like the static_cast. Additionally, it performs runtime checking to ensure that casting is successful. If casting fails, it returns NULL. So, if you run the following code,

1 Rectangle rectangle(5, 3);
2 GeometricObject* p = &rectangle;
3 Circle* p1 =
dynamic_cast<Circle*>(p);
4 cout << (*p1).getRadius() << endl;

p1 will be NULL. A runtime error will occur when running the code in line 4. Recall that NULL is defined as 0, which indicates that a pointer does not point to any object. The definition of NULL is in a number of standard libraries including <iostream> and <cstddef>.

Now you can rewrite the displayGeometricObject function using dynamic casting, as in Listing 15.20, to check whether casting is successful at runtime.

Listing 15.20 DynamicCastingDemo.cpp

1 #include “AbstractGeometricObject.h”
2 #include “DerivedCircleFromAbstractGeometricObject.h”
3 #include “DerivedRectangleFromAbstractGeometricObject.h”
4 #include <iostream>
5
using namespace std;
6
7
// A function for displaying a geometric object
8 void displayGeometricObject(GeometricObject& g)
9 {
10    cout <<
“The area is ” << g.getArea() << endl;
11    cout <<
“The perimeter is ” << g.getPerimeter() << endl;
12
13    GeometricObject* p = &g;
14    Circle* p1 =
dynamic_cast<Circle*>(p);
15    Rectangle* p2 =
dynamic_cast<Rectangle*>(p);
16
17   
if (p1 != NULL)
18    {
19       cout <<
“The radius is ” << p1->getRadius() << endl;
20       cout <<
“The diameter is ” << p1->getDiameter() << endl;
21    }
22
23   
if (p2 != NULL)
24    {
25       cout <<
“The width is ” << p2->getWidth() << endl;
26       cout <<
“The height is ” << p2->getHeight() << endl;
27    }
28 }
29
30
int main()
31 {
32     Circle circle(
5);

33     Rectangle rectangle(5, 3);
34
35     cout <<
“Circle info: ” << endl;
36     displayGeometricObject(circle);
37
38    cout <<
“\nRectangle info: ” << endl;
39    displayGeometricObject(rectangle);
40
41   
return 0;
42 }

Line 13 creates a pointer for a GeometricObject g. The dynamic_cast opera­tor (line 14) checks whether pointer p points to a Circle object. If so, the object’s address is assigned to p1; otherwise p1 is NULL. If p1 is not NULL, the getRadius() and getDiameter() functions of the Circle object (pointed by p1) are invoked in lines 19-20. Similarly, if the object is a rectangle, its width and height are displayed in lines 25-26.

The program invokes the displayGeometricObject function to display a Circle object in line 36 and a Rectangle object in line 39. The function casts the parameter g into a Circle pointer p1 in line 14 and a Rectangle pointer p2 in line 15. If it is a Circle object, the object’s getRadius() and getDiameter() functions are invoked in lines 19-20. If it is a Rectangle object, the object’s getWidth() and getHeight() functions are invoked in lines 25-26.

The function also invokes GeometricObject’s getArea() and getPerimeter() functions in lines 10-11. Since these two functions are defined in the GeometricObject class, there is no need to downcast the object parameter to Ci rcl e or Rectangl e in order to invoke them.

Source: Liang Y. Daniel (2013), Introduction to programming with C++, Pearson; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *