C++98corebeginner
Run-Time Type Information (RTTI)
C++98 · The Foundation · Compiler flag: -std=c++98
Explanation
Identifies object types at runtime using `typeid` or `dynamic_cast`.
Real-World Scenario
Log object types in a debugging tool.
Quick Snippet
snippet.cpp
#include <typeinfo> typeid(obj).name();Full Example
rtti.cpp
// CodeNebula - C++98 Example: RTTI (Runtime Type Identification)
// Compile: g++ -std=c++98 -Wall -Wextra -o example rtti.cpp
// RTTI allows programs to determine the actual type of an object at runtime.
// C++98 provides two RTTI mechanisms:
// 1. dynamic_cast - safely downcast polymorphic pointers/references
// 2. typeid - get the type_info of an expression
// RTTI requires at least one virtual function in the class hierarchy.
#include <iostream>
#include <typeinfo> // Required for typeid and type_info
#include <string>
// ------------------------------------------------------------------
// Polymorphic base class (virtual function enables RTTI)
// ------------------------------------------------------------------
class Shape {
public:
virtual double area() const = 0; // pure virtual
virtual std::string name() const { return "Shape"; }
virtual ~Shape() {}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const { return 3.14159265 * radius * radius; }
std::string name() const { return "Circle"; }
double getRadius() const { return radius; }
};
class Rectangle : public Shape {
private:
double w, h;
public:
Rectangle(double width, double height) : w(width), h(height) {}
double area() const { return w * h; }
std::string name() const { return "Rectangle"; }
double getWidth() const { return w; }
double getHeight() const { return h; }
};
class Triangle : public Shape {
private:
double base, height;
public:
Triangle(double b, double h) : base(b), height(h) {}
double area() const { return 0.5 * base * height; }
std::string name() const { return "Triangle"; }
double getBase() const { return base; }
double getHeight() const { return height; }
};
// Derived from Circle
class Sphere : public Circle {
private:
// Sphere extends Circle, using radius for a 3D calculation
public:
Sphere(double r) : Circle(r) {}
double area() const {
// Surface area of a sphere
return 4.0 * 3.14159265 * getRadius() * getRadius();
}
std::string name() const { return "Sphere"; }
double volume() const {
double r = getRadius();
return (4.0 / 3.0) * 3.14159265 * r * r * r;
}
};
// ------------------------------------------------------------------
// Function that uses dynamic_cast for safe downcasting
// ------------------------------------------------------------------
void describeShape(Shape* shape) {
std::cout << " Shape name: " << shape->name()
<< ", area: " << shape->area() << std::endl;
// Try to downcast to specific types using dynamic_cast
Circle* circle = dynamic_cast<Circle*>(shape);
if (circle != 0) {
std::cout << " -> It is a Circle with radius " << circle->getRadius() << std::endl;
// Check if it's specifically a Sphere (further down the hierarchy)
Sphere* sphere = dynamic_cast<Sphere*>(shape);
if (sphere != 0) {
std::cout << " -> Actually a Sphere, volume: " << sphere->volume() << std::endl;
}
}
Rectangle* rect = dynamic_cast<Rectangle*>(shape);
if (rect != 0) {
std::cout << " -> It is a Rectangle "
<< rect->getWidth() << "x" << rect->getHeight() << std::endl;
}
Triangle* tri = dynamic_cast<Triangle*>(shape);
if (tri != 0) {
std::cout << " -> It is a Triangle, base=" << tri->getBase()
<< " height=" << tri->getHeight() << std::endl;
}
}
// ------------------------------------------------------------------
// Function using typeid for type comparison
// ------------------------------------------------------------------
void identifyType(const Shape& shape) {
std::cout << " typeid name: " << typeid(shape).name() << std::endl;
// Compare types using typeid
if (typeid(shape) == typeid(Circle)) {
std::cout << " Exact type is Circle" << std::endl;
} else if (typeid(shape) == typeid(Rectangle)) {
std::cout << " Exact type is Rectangle" << std::endl;
} else if (typeid(shape) == typeid(Triangle)) {
std::cout << " Exact type is Triangle" << std::endl;
} else if (typeid(shape) == typeid(Sphere)) {
std::cout << " Exact type is Sphere" << std::endl;
} else {
std::cout << " Unknown derived type" << std::endl;
}
}
// ------------------------------------------------------------------
// Function that uses dynamic_cast with references (throws on failure)
// ------------------------------------------------------------------
void tryAsCircle(Shape& shape) {
try {
Circle& c = dynamic_cast<Circle&>(shape);
std::cout << " Successfully cast to Circle, radius="
<< c.getRadius() << std::endl;
}
catch (const std::bad_cast& e) {
std::cout << " bad_cast: not a Circle (" << e.what() << ")" << std::endl;
}
}
int main() {
std::cout << "=== C++98 RTTI (Runtime Type Identification) ===" << std::endl;
// Create shape objects
Circle circle(5.0);
Rectangle rect(4.0, 6.0);
Triangle tri(3.0, 8.0);
Sphere sphere(3.0);
// --- 1. dynamic_cast with pointers ---
std::cout << "\n--- 1. dynamic_cast with pointers ---" << std::endl;
Shape* shapes[4];
shapes[0] = &circle;
shapes[1] = ▭
shapes[2] = &tri;
shapes[3] = &sphere;
for (int i = 0; i < 4; ++i) {
std::cout << "\nshapes[" << i << "]:" << std::endl;
describeShape(shapes[i]);
}
// --- 2. dynamic_cast returns NULL on failure ---
std::cout << "\n--- 2. dynamic_cast NULL check ---" << std::endl;
Shape* s = ▭
Circle* asCircle = dynamic_cast<Circle*>(s);
if (asCircle == 0) {
std::cout << "Rectangle is NOT a Circle (dynamic_cast returned NULL)" << std::endl;
}
s = &circle;
asCircle = dynamic_cast<Circle*>(s);
if (asCircle != 0) {
std::cout << "Circle IS a Circle, radius=" << asCircle->getRadius() << std::endl;
}
// Sphere is-a Circle (inheritance)
s = &sphere;
asCircle = dynamic_cast<Circle*>(s);
if (asCircle != 0) {
std::cout << "Sphere IS-A Circle, radius=" << asCircle->getRadius() << std::endl;
}
// --- 3. typeid for type identification ---
std::cout << "\n--- 3. typeid identification ---" << std::endl;
for (int i = 0; i < 4; ++i) {
std::cout << "shapes[" << i << "]:" << std::endl;
identifyType(*shapes[i]);
}
// --- 4. typeid comparison ---
std::cout << "\n--- 4. typeid comparisons ---" << std::endl;
// Note: typeid on polymorphic types gives the DYNAMIC type
std::cout << "typeid(circle) == typeid(Circle): "
<< (typeid(circle) == typeid(Circle) ? "true" : "false") << std::endl;
// Expected: true
// Sphere is NOT the same type as Circle (typeid checks exact type)
std::cout << "typeid(sphere) == typeid(Circle): "
<< (typeid(sphere) == typeid(Circle) ? "true" : "false") << std::endl;
// Expected: false
std::cout << "typeid(sphere) == typeid(Sphere): "
<< (typeid(sphere) == typeid(Sphere) ? "true" : "false") << std::endl;
// Expected: true
// Via pointer - typeid dereferences to get dynamic type
Shape* sp = &sphere;
std::cout << "typeid(*sp) == typeid(Sphere): "
<< (typeid(*sp) == typeid(Sphere) ? "true" : "false") << std::endl;
// Expected: true
// --- 5. dynamic_cast with references ---
std::cout << "\n--- 5. dynamic_cast with references ---" << std::endl;
tryAsCircle(circle);
// Expected: Successfully cast to Circle, radius=5
tryAsCircle(rect);
// Expected: bad_cast: not a Circle
tryAsCircle(sphere);
// Expected: Successfully cast to Circle, radius=3
// --- 6. type_info::before() for ordering ---
std::cout << "\n--- 6. type_info ordering ---" << std::endl;
const std::type_info& ti1 = typeid(Circle);
const std::type_info& ti2 = typeid(Rectangle);
std::cout << "Circle before Rectangle? "
<< (ti1.before(ti2) ? "yes" : "no") << std::endl;
std::cout << "Rectangle before Circle? "
<< (ti2.before(ti1) ? "yes" : "no") << std::endl;
// Results are implementation-defined but consistent
return 0;
}
/*
Expected Output:
=== C++98 RTTI (Runtime Type Identification) ===
--- 1. dynamic_cast with pointers ---
shapes[0]:
Shape name: Circle, area: 78.5398
-> It is a Circle with radius 5
shapes[1]:
Shape name: Rectangle, area: 24
-> It is a Rectangle 4x6
shapes[2]:
Shape name: Triangle, area: 12
-> It is a Triangle, base=3 height=8
shapes[3]:
Shape name: Sphere, area: 113.097
-> It is a Circle with radius 3
-> Actually a Sphere, volume: 113.097
--- 2. dynamic_cast NULL check ---
Rectangle is NOT a Circle (dynamic_cast returned NULL)
Circle IS a Circle, radius=5
Sphere IS-A Circle, radius=3
--- 3. typeid identification ---
(typeid names are implementation-defined)
--- 4. typeid comparisons ---
typeid(circle) == typeid(Circle): true
typeid(sphere) == typeid(Circle): false
typeid(sphere) == typeid(Sphere): true
typeid(*sp) == typeid(Sphere): true
--- 5. dynamic_cast with references ---
Successfully cast to Circle, radius=5
bad_cast: not a Circle
Successfully cast to Circle, radius=3
--- 6. type_info ordering ---
(ordering is implementation-defined)
*/
Run command
g++ -std=c++98 -Wall -Wextra -o example rtti.cpp