About non-intrusive polymorphism

Polymorphism in C++

C++ implements subtyping polymorphism in the form of virtual functions, member functions that should be overridden on derived classes, in a way each class implements its own behavior for the function:

In the example above, the Interface class defines a pure virtual function f() that all the hierarchy members should override. Now you can play with objects of that hierarchy in a uniform way, being sure all of those have a f() function of its own.

One of the most common uses cases of polymorphism is to be able to store multiple heterogeneous objects into the same container and then operate on them on the same way. While virtual ensures each object acts with its correct behavior. This is usually achieved with dynamic allocation: Allocating objects and the use pointers to the base class to refer to them. This way we can refer to any object of the hierarchy using the same pointer type.

This simple “Inherit and dynamically allocate” approach works like a charm, but is very error prone. Did you noticed the memory leak in the example? Also suffers from a little culprint that, IMHO, is not that little: Inheritance. To make all the sorcery work, you should belong to a class hierarchy. What if I want polymorphic behavior for existing types? What if I want polymorphic functions for int? Being bound to a class hierarchy is not the way to go. I’m looking at you Java.

Fortunately we are using C++, a multi-paradigm programming language that allows you to write very smart tricks. Meet non-intrusive polymorphism.

To : or not to :

What if you can find a way to provide that kind of polymorphism, a function with a specific implementation for each type and at the same type have easy heterogeneous storage?

Bonus: No dynamic memory management, just RAIIfied polymorphism from the beginning.

The fundamentals are simple: Hide all the polymorphism intricacies to the user. We will do exactly the same inheritance, base class, dynamic allocation as above, but all closed inside the Poly class.

Lets look at the internals of Poly:

A pointer to a dynamically-allocated object and a f() member function that calls the f() from the object. As I said, all polymorphism will be managed inside the Poly class internally. The point is that having to inherit from a base class to achieve polymorphism is an implementation detail, only based on how the language works.

The trick consists in being able to store any kind of data dynamically, instead of explicitly asking that type to belong to the base hierarchy. Come in templates!

Now each type we introduce into Poly has its own implementation of f(). In this example, I supposed there’s a viable free f() function taking a T as parameter. As long as your type has a valid f() overload, template, whatever, your type can be used in Poly.
Here’s an example:

Some details

The constructor of Poly does part of the trick: It gets a value and instances the correct derived class dynamically:

 

Also don’t forget to follow The Rule Of Five/Three and implement all the special member functions required for correct value semantics of Poly objects. Use a smart pointer instead or raw new/delete if you like.

References

“Inheritance is The Base Class of Evil”, Sean Parent


Related Posts
  • Denis Barakhtanov

    Nice Idea. But what if we have a large class hierarchy, for instance 15 classes and each has three polymorphic functions ? Seems then we should have 45 free functions and what if we need to use a class implementation details in these function ?

    What about code structure, will it spread over entire project ? I mean if I have a class A I know that it is in source file A.h / A.cpp, where then should I place implementation of polymorphic function, in place where it going to be used ?

    Are there any other advantages besides simplifying the control of object’s life cycle ? Maybe much easier to keep in vector shard_ptr instead of raw pointer ?

    There are some typos in article, like using “overrides” instead of “override” keyword, “std::vector objects;” – won’t compile because it’s abstract class (should be std::vector) and in the penultimate code sample argument of function f should be “const std::string &str”.

  • Pablo Marcos

    Great idea Manu, but I would like to see a compiling example to be able to play with it. Therefore, instead of seeing “…” in each block, I would appreciate to see a at least at the very end of the article a minimal example fully working.

    Apart from the typos pointed out by Denis, I see the following:

    – ptr or _ptr?
    – derived should derive from base
    – Poly seems to need a “void f()” method that calls ptr->f()
    – vonst should be const
    – “hello world!”s. What about the ending “s”?

    This is what I ended up with:
    http://goo.gl/ps7ky6 – to test in an online compiler
    http://pastebin.com/jDcgqCqi – to see it in pastebin

    Please don’t take this as a negative comment but as a constructive one, which is what it intends to so that the following articles are even better. Keep up the good work!