// Copyright 2026, University of Freiburg
// Chair of Algorithms and Data Structures
// Author: Hannah Bast <bast@cs.uni-freiburg.de>
#include <iostream>
#include <string>
#include <vector>
// A "thing" with a method `toString()` and not much else. Uses for explaining
// inheritance and polymorphism in C++.
class Thing {
public:
// Returns a string representation of this thing.
virtual std::string toString() const { return "THING"; }
// Virtual destructor that does nothing so that the destructors of the
// subclasses get called when they should.
virtual ~Thing() {}
};
// An "integer thing" that inherits from `Thing` and holds an integer.
class IntegerThing : public Thing {
public:
// Create an `IntegerThing` with the given value.
IntegerThing(int value) { value_ = new int(value); }
// IntegerThing(int value) : value_(value) {}
// Destructor to free the allocated memory.
~IntegerThing() { delete value_; }
// Returns a string representation of this integer thing.
std::string toString() const override { return std::to_string(*value_); }
private:
// The int value.
int *value_;
// int value_;
};
// A "string thing" that inherits from `Thing` and holds a string.
class StringThing : public Thing {
public:
// Create a `StringThing` with the given value.
StringThing(const char *s) { content_ = s; }
// Returns a string representation of this string thing.
std::string toString() const override { return content_; }
private:
// The string value.
std::string content_;
};
// Main program for various demos.
int main() {
std::cout << std::endl;
// Create a number of things and print them.
{
Thing thing1;
IntegerThing thing2(42);
StringThing thing3("doof");
std::cout << thing1.toString() << std::endl;
std::cout << thing2.toString() << std::endl;
std::cout << thing3.toString() << std::endl;
std::cout << std::endl;
}
// Show the sizes of each of the three classes.
{
std::cout << "sizeof(Thing) : " << sizeof(Thing) << std::endl;
std::cout << "sizeof(IntegerThing) : " << sizeof(IntegerThing) << std::endl;
std::cout << "sizeof(StringThing) : " << sizeof(StringThing) << std::endl;
std::cout << std::endl;
}
// Create a vector of things and print them.
{
std::vector<Thing> things;
Thing thing1;
IntegerThing thing2(42);
StringThing thing3("doof");
things.push_back(thing1);
things.push_back(thing2);
things.push_back(thing3);
for (const Thing &thing : things) {
std::cout << thing.toString() << std::endl;
}
std::cout << std::endl;
}
// The same thing, now with pointers to things.
{
std::vector<Thing *> things;
Thing thing1;
IntegerThing thing2(42);
StringThing thing3("doof");
things.push_back(&thing1);
things.push_back(&thing2);
things.push_back(&thing3);
for (const Thing *thing : things) {
std::cout << thing->toString() << std::endl;
}
std::cout << std::endl;
}
// Show what can happen with inheritance and destructors.
{
// IntegerThing thing(42);
Thing *thing;
thing = new IntegerThing(42);
delete thing;
}
// Show RTTI stuff (runtime type information).
{
Thing *thing = new IntegerThing(42);
IntegerThing *integerThing = dynamic_cast<IntegerThing *>(thing);
StringThing *stringThing = dynamic_cast<StringThing *>(thing);
// IntegerThing *integerThing = static_cast<IntegerThing *>(thing);
// StringThing *stringThing = static_cast<StringThing *>(thing);
std::cout << "Adress of thing : " << thing << std::endl;
std::cout << "Adress of integerThing : " << integerThing << std::endl;
std::cout << "Adress of stringThing : " << stringThing << std::endl;
std::cout << std::endl;
delete thing;
}
return 0;
}