My approach to learning C++ runtime polymorphism

Srecko Kostic
Dev Genius
Published in
5 min readSep 24, 2022

--

My brief story with implementation reasons. I hope that you can get an idea of what I did. I created a design document. That would hopefully help others understand my solution. Here is the link to the Employee Factory source code.

Source Code for Employee Factory Thornhil Indrustries that accept employees and starts work hours
Source Code for Employee Factory Thornhil Indrustries that accept employees and starts work hours

Preface

I suggest checking the code first. However, you can continue without it.

I learned about runtime polymorphism in the C++ book. The concept was unfamiliar but interesting. To purge that feeling, I created a story whose solution is runtime polymorphism. Aside from that, I wanted to absorb good C++ practices.

The pull request with the quality context

Here is my pull request for Employee Factory with more context than this post.

My approach

I learned about runtime polymorphism. To learn it, I had to spend some time working on it. I believe this is what everyone calls experience. I remember very well every problem I was stuck on for days. It is almost like they get engraved in my mind. So I had this idea, I find a problem for a solution. Then I solve the problem using that solution.

The C++ factory source code

The factory can take any Employee. It doesn’t care about Employee implementation.

The problem

The factory employs many employees. Employees have one of these roles: cleaning, checking product quality and testing samples, maintaining equipment, operating forklifts, and aligning workpieces. Each employee has different job responsibilities, but they all work during work hours.

The solution

We will implement the Factory facility. Such a facility will hold all employees and start work hours once everyone is ready.

The facility doesn’t need to know about the Employee’s role. Every employee at the facility is expected to work. What he does is defined by his role. The facility may have many janitors with different names. They have different property values but they do the same work.

Class Employee is an interface that declares what an employee does.

Classes Janitor, Tester, Maintenance, and Operator define Employee’s behavior.

Implementation decisions

Use design document

Design Document Title and Description

I want to try out the design-first approach. I believe it is much easier to Implement existing solutions using code. Code-first approach produces a mess. I can tell that from experience.

Use CMake build system

The Root CMakeLists file

Using build systems is part of the real-life workflow. Of course, I had to use the build system. CMake is a cross-platform build system. It helped me work on the project using Linux and Windows.

Use strict compilation flags

The C++ Strict Compilation Flags

I’ve read somewhere it’s advised to use strict compilation flags. They helped me learn about new concepts.

Use employee as a pure interface

Employee Interface

Semantically, the interface declares WHAT is possible, not how. Data should be elsewhere. Therefore I refactored the solution. Use the interface to declare what is possible. Use class Person to declare shared properties.

Use virtual destructor

The compiler warned me that a base class must have a virtual destructor. That makes sense because when we use pointers we won’t have it in the vtable. I solved it using the default virtual destructor because I’m not using heap allocation inside. The default destructor should be sufficient.

Include used headers

I learned about IWYU — Include What You Use.
https://google.github.io/styleguide/cppguide.html#Include_What_You_Use

Use target compile options

I used add_compile_options which adds them for all targets. Using target_compile_options allows using different options per target. For example tests.

Use #pragma once

I used #ifndef because of the Google C++ style guide. A friend reviewed my code and said pragma once is used in practice. I replaced #ifndef because I don’t have good reasons not to.

Remove class person

The only class person use case was property inheritance. That made sense if I look at the hierarchy top-down. I talked to a friend and from a new perspective, it doesn’t make sense. A more sensible approach is to define a factory that accepts employees. Afterward, I can implement an employee interface when I need a new employee.

Update implementation of employees

Remove class Person and update employee implementation. Each employee implementation inherited Person and Employee. Person allowed sharing properties, and Employee is an interface. The new way to look at it is, to implement a role that fulfills obligations that Employee promises to fulfill.

Reflect solution using a modular structure

Code Structure

Make the solution more expressive by dividing it into modules. The solution should be more obvious to the reader. Code structure should help us express our solution. Stop polluting the global namespace and modularize logic. Using namespace we don’t pollute the global ones.

Split files to interface and implementation

Treat every header as an interface. I find that a much cleaner approach than having an interface directory for employees. I did that because The Employee is an abstract class. However, it is a much cleaner approach to use the `impl` directory for implementation and treat all headers as interfaces.

Take unique_ptr as rvalue reference

The Factory Class Accepts Anonymous Unique Pointer

Accept employes as rvalue to consume them. I wanted to consume anonymous objects. I couldn’t consume anonymous objects because I didn’t know how rvalue works.

--

--

I create content from my perspective on topics I learn. Some information may be incorrect, but I hope some find my content useful.