News:

This week IPhone 15 Pro winner is karn
You can be too a winner! Become the top poster of the week and win valuable prizes.  More details are You are not allowed to view links. Register or Login 

Main Menu

C++ tips - Inheritance and using value-based ADTs

Started by ben2ong2, October 07, 2006, 09:44:06 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

ben2ong2

PROBLEM: You are not allowed to view links. Register or Login (John Max Skaller), 1 May 93

A question I have been grappling with recently is: why doesnt public
inheritance in C++ work properly?

Consider:

   class Employee {
      float salary;
   public:
      Employee(float x) : salary(x) {}
      virtual float annual() {return salary; }
      float weekly {return salary/52.0; }
   };

   class Manager : public Employee {
      float bonus;
   public:
      Manager(float x, float b) : Employee(x), bonus(b) {}
      float annual() {return salary + bonus; }
   };

Well, a Manager 'isA' Employee, isnt e? ( e= he/she)

   Employee * pe = new Manager(100000.0,20000.0);
   cout<<pe->annual();

Thats correct: polymorphism. Now consider:

   static float total=0.0;
   void AddStaffCost(Employee e)
   {
      total+=e.annual();
   }

WOOPS! We've sliced off the Managers bonus. There are two things
(that I can think of quickly) we can do to fix this:

   a) make the copy constructor private (or unimplemented)
   b) make the upcast from Manager to Employee private

Case a: private copy constructor:
----------------------------------

   class Employee {
      float salary;
      Employee(const Employee&);
   public:
      Employee(float x) : salary(x) {}
      virtual float annual() {return salary; }
      float weekly {return salary/52.0; }
   };

This is sufficient to ensure that no class derived from Employee
can be copied. (Yes, I neglected assignment :-)
Such a class is a proper object class: it
cannot be used like a value (ADT). Its address (this pointer)
is sacred: other entities can point to an instance and the
instance is uniquely that Employee.

A derived class of Employee such as Manager is an Employee
to greater extent than the original Employee class.

Case b: Disallow upcasting.
---------------------------

   class Manager : private Employee {
      float bonus;
   public:
      Employee::weekly;
      Manager(float x, float b) : Employee(x), bonus(b) {}
      float annual() {return salary + bonus; }
   };

The private base prevents upcasting. We 'reuse' the code and state
of Employee, but not its whole interface. The access declaration
is a shorthand for a wrapper function:

   class Manager : private Employee {
      float bonus;
   public:
      float weekly() { return Employee::weekly(); }
      Manager(float x, float b) : Employee(x), bonus(b) {}
      float annual() {return salary + bonus; }
   };

from which point of view you can see that while the code and state
of Employee is reused, the interface is not. None of it.

This Manager is a completely different type from an Employee,
and it cant be used as an Employee. This Manager "is not a" Employee.

It is however, a fully fledged ADT class that can be freely copied.

What I see the glimmers of here is the following: there are two
distinct type of classes: ADTs and Objects. You cant have it
both ways.

In the Object Heirarchy approach, one uses public inheritance to
create new  classes that are *both* ADTs and also Objects. In other
languages like Smalltalk, the derived classes are genuine objects.
(You dont have to copy the objects because you have garbage collection)

In C++, unless you disable copying, you are exposing yourself
to an abuse not possible in pure object oriented languages.

The correct way to us a derived ADT 'as a' base is probably
to create a conversion operator, if that is desired.

Creating new ADT classes is a pain because you *cant* use
public inheritance: you have to use private inheritance
and 're-export' the interface you want to be reused.
Perhaps this a weakness in C++? Or are access declarations
adequate? (They certainly dont help with delegation :-)

I think it is possible to use public inheritance for ADT classes
if you add functions only and no more data. If these functions
could have been written to use only the public interface
of the base, then they could have been global functions:
the derivation is a convenience only. Even if slicing occurs,
no part of the state is lost.



You are not allowed to view links. Register or Login
You are not allowed to view links. Register or Login