Inheritance/Polymorphism example

This is an example of inheritance and polymorphism, two ideas of Object-Oriented Programing.

Attribute is an abstract class that is implemented by StringAttr and IntegerAttr. This allows us to use containers like std::vector or std::list to store both of these types.

Just like the other example, this one has no leaks or warnings.

NOTE: I’m not saying this is attributes should be implemented. I’m just using it as a means to explain these features.

#include <stdio.h> #include <climits> #include <vector> #include <string> #include <string.h> #include <memory> class Attribute { public: Attribute(const char *n); char * toString() const; // const after the arguments means that this function does not modify anything internal to Attribute virtual ~Attribute(); // a virtual destructor must be defined so the derrived classes' destructors are called. char * name; // every attribute has a name, we should define it once here. protected: virtual char * toString_impl() const = 0; // the = 0 at the end makes this a pure-virtual function. // Pure-virtual functions must be overrided by derrived classes. // Having a pure-virtual function makes this class an Abstract Class }; Attribute::Attribute(const char *n) { name = strdup(n); printf("Creating attribute with name %s\n", name); } Attribute::~Attribute() { free(name); } // Returns an allocated string, must be freed by caller // This is a "wrapper" to all the attribute's toString_impl(). char * Attribute::toString() const { printf("Calling toString on %s\n", name); return toString_impl(); } class StringAttr : public Attribute { public: StringAttr(const char *n, const char *s); ~StringAttr(); protected: char * toString_impl() const override; // override tells us that this function overrides the base class private: char * value; }; StringAttr::StringAttr(const char *n, const char *s) : Attribute(n) // Call Attribute's (const char *) constructor { printf("Creating String Attr\n"); value = strdup(s); // Since we're already calling Attributes constructor, we just need to fill in the value } StringAttr::~StringAttr() { free(value); } char * StringAttr::toString_impl() const { char s[100]; snprintf(s, sizeof(s), "%s %s", name, value); return strdup(s); } class IntegerAttr : public Attribute { public: IntegerAttr(const char *n, const char *s); IntegerAttr(const char *n, int val); protected: char * toString_impl() const override; private: int value; }; IntegerAttr::IntegerAttr(const char *n, const char *s) : Attribute(n) { printf("Creating Integer Attr\n"); value = strtol(s, nullptr, 10); } IntegerAttr::IntegerAttr(const char *n, int val) : Attribute(n) { printf("Creating Integer Attr\n"); value = val; } char * IntegerAttr::toString_impl() const { char s[100]; snprintf(s, sizeof(s), "%s %d", name, value); return strdup(s); } int main() { std::vector<Attribute *> attrs; attrs.push_back(new StringAttr("jobid", "3.abcd")); // We can "upcast" StringAttr to the Attribute base class. attrs.push_back(new IntegerAttr("stime", "1600001031")); attrs.push_back(new StringAttr("select", "1:ncpus=1")); attrs.push_back(new IntegerAttr("qtime", 1600003201)); // attrs.push_back(new Attribute("badattr")); // This fails because Attribute is an abstract class and cannot be instantiated for (const auto &attr : attrs) { char * out = attr->toString(); if (auto p = dynamic_cast<StringAttr *>(attr)) { // We can "downcast" Attributes to their derived class. printf("String Attribute! [%s]\n", out); } else if (auto p = dynamic_cast<IntegerAttr *>(attr)) { printf("Integer Attribute! [%s]\n", out); } else { printf("%s\n", out); } free(out); } for (auto &attr : attrs) { delete(attr); } }

This outputs:

Creating attribute with name jobid Creating String Attr Creating attribute with name stime Creating Integer Attr Creating attribute with name select Creating String Attr Creating attribute with name qtime Creating Integer Attr Calling toString on jobid String Attribute! [jobid 3.abcd] Calling toString on stime Integer Attribute! [stime 1600001031] Calling toString on select String Attribute! [select 1:ncpus=1] Calling toString on qtime Integer Attribute! [qtime 1600003201]