Search code examples
c++abstract-classvirtual

C++ passing base type to pure virtual function


i want to understand the behavior of pure virtual functions in derived class when passing to it an argument of same type as (abstract) base class.

to clarify the question, i took the following code from GeeksForGeeks and modified it:

namespace example {
enum Type {ENGINEER, MANAGER};
class Employee
{
private:    
    const Type worker;
public:
    Employee(const Type& worker) : worker(worker) {}
    virtual ~Employee {}

    virtual void raiseSalary(const Employee&) = 0;
    {  /* common raise salary code */  }

    virtual void promote(const Employee&) = 0;
    { /* common promote code */ }
};

class Manager: public Employee {
    private:
        int degree;
    public:
        //<constructor>\\

    virtual void raiseSalary(const Employee&)
    {  /* Manager specific raise salary code, may contain
          increment of manager specific incentives*/  }

    virtual void promote(const Employee&)
    { /* Manager specific promote */ }
};

}

Now, how can we get access to the field degree in derived class Manager inorder to update his degree? since the passed argument to raiseSalary(Employee& employee) could be Manager or Engineer


Solution

  • I think there are two ways to handle that problem. Let's start with some really bad solution: using casting. In that case dynamic_cast. You can try to down cast a type. If dynamic_cast isn't able to do that it is going to return a null pointer or throw an exception (depends on wheather you cast a pointer or a value/reference type). But that approach is going to force you to adapt your casts as more Manager, Engineer types are going to come. You might also need to use friend to allow specific classes to access internals of others. friend is not going to be inherited in the hierarchy, so you are going to end up with many friends => broken, broken, broken :(

    An alternative would be to use the Visitor Pattern: http://en.wikipedia.org/wiki/Visitor_pattern Using the visitor pattern you can also make a base no-op visitor and finer grained Visitors to handle specific stuff. Just a small example (with specific visitors without derivation):

    namespace example {
    
      class SalaryRaisingVisitor;
      class EmployeePromotingVisitor;
    
      class Employee
      {
      public:
          Employee() {}
          //don't forget to implement the copy constructor: read more about rule of 3!!!
    
          virtual ~Employee {}
    
          virtual void accept(SalaryRaisingVisitor const&) = 0;
          virtual void accept(EmployeePromotingVisitor const&) = 0;
      };
    
      class Manager: public Employee {
          private:
              int degree;
          public:
              //<constructorS>
    
          virtual void accept(SalaryRaisingVisitor const& v)
          {
            v.visit(*this, degree); 
          }
    
          virtual void accept(EmployeePromotingVisitor const& v)
          {
            v.visit(*this, degree);
          }
      };
    
      class Engineer: public Employee {
          public:
              //<constructorS>
    
          virtual void accept(SalaryRaisingVisitor const& v)
          {
            v.visit(*this); 
          }
    
          virtual void accept(EmployeePromotingVisitor const& v)
          {
            v.visit(*this);
          }
      };
    
      class SalaryRaisingVisitor
      {
        void visit(Manager& m, int& degree) //might be const if no internal state changes
        {
          //...
        }
    
        void visit(Engineer& e) //might be const if no internal state changes
        {
          //...
        }
      };
    
    }
    

    At the end as you deal with C++, try to avoid virtual functions :) and move everything to static polymorphism :)