Channels ▼
RSS

Security

Simulating Polymorphic Operators in C++

Source Code Accompanies This Article. Download It Now.


Version 3: An Enumerated Type

My final version replaces the dynamic_cast operator with a type identifier added to the parent class. No virtual method is needed for any of the classes. This lets me reduce the memory required for each object from a 32-bit pointer pointing to the vtable to an 8-bit character that stores the type identifier. In addition, no memory for the class's vtable is needed since a virtual method is not used nor is it required for the dynamic_cast operator because this operator is also not being used. The type identifier is an enumerated type stored in a character variable that is added to the Employee class; see Listing Five.

enum type { EMPLOYEE=1, MANAGER=2, SALESPERSON=3 };
class Employee
{
public:
    char _type;
    Employee()  { _type= EMPLOYEE; }
    friend ostream& operator<<(ostream& os, const Employee& aEmployee);
};
class Manager : public Employee
{
public:
    Manager() { _type = MANAGER; }
    friend ostream& operator<<(ostream& os, const Manager& aManager);
};
Listing Five

Objects must initialize the type identifier in their constructors. For brevity, I made the type identifier public but it could also be made private. Protected accessor and mutator methods would also be supplied for this option.

The new type identifier serves two purposes—it identifies the type of object passed to the parent's insertion function, and it replaces the Boolean flag used in the previous version. The flag stops the infinite recursion between the two insertion functions. The type identifier can act as a flag if it is altered while the two functions are executing, but reset prior to exiting the insertion operation.

Listing Six is version 3 of the example. The code for the Employee insertion function extends the previous examples so that it works for a situation where the Employee class has two children—a Manager and a Salesperson class. The Employee insertion function processes the Employee portion of the class before checking the type identifier. If a Manager object was passed into the Employee function, the type identifier is changed, then the Manager's function is called.

ostream& operator<<(ostream& os, const Employee& aEmployee)
{
// code to print the Employee class
    if (aEmployee._type == MANAGER)
    {
        const_cast<Employee&>(aEmployee)._type = - aEmployee._type;
        os << static_cast<const Manager&>(aEmployee);
        const_cast<Employee&>(aEmployee)._type = - aEmployee._type;
    }
    else if (aEmployee._type == SALESPERSON)
    {
        const_cast<Employee&>(aEmployee)._type = - aEmployee._type;
        os << static_cast<const Manager&>(aEmployee);
        const_cast<Employee&>(aEmployee)._type = - aEmployee._type;
    }
    return os;
}
ostream& operator<<(ostream& os, const Manager& aManager)
{
    if (aManager._type == MANAGER)
    {
        const_cast<Manager&>(aManager)._type = - aManager._type;
        os << static_cast<const Employee&>(aManager);
        const_cast<Manager&>(aManager)._type = - aManager._type;
    }
// code to print the Manager class
    return os;
}
Listing Six

The Manager's insertion function checks the object's type identifier before calling the Employee's insertion function. If the type identifier has been changed, then we know that the Manager's function was called by its parent and, thus, should not be called again. If the type identifier has not been modified, then we know it's the nonpolymorphic case, and therefore, we will have to call the Employee's insertion function to process the Employee portion of the object.

While this behavior appears equivalent to version 2, the behavior is actually slightly improved. Version 2 calls the Employee's function twice for the first example (cout << *pEmp). However, version 3 does not call the Employee function a second time because the Manager's function sees that the Employee's function changed the type identifier. Replacing version 2's static function variable with a class variable allows for this improvement.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video