Solved

Need help with derived types

Posted on 2006-11-22
22
518 Views
Last Modified: 2012-08-14
I have a bas class Employee  and three derived types (Hourly,Salary,PieceWork) and a container Employees

I know many of you have seen this problem Space before, we got an extension on the assignment so I wanted to clean some things up.

In the base class I have the member
protected:
     enum EmployeeType {salaried,hourly,piecework}; // derived types
public:
     virtual EmployeeType GetType() const = 0;

Its purpose is to be able to sort each type in my sort algorithm

In my container i made a printByGroup  service
ie

void Employees::printByGroup{
    // declare subtotals accumalators
    // declart totals acummalators
    // call sortByGroup
     for(i=0;i<EmployeeCount,i++)
     {
         if(all[i].getType==0) //Hourly type
         {        // Print Hourly Headers  
                  // Do Hourly subtotals
                  //add to accummalators
         }
         //print subtotals for are Hourly
    }
     
   I do that for all 3 types ( 5 pages of code)
   I know that is not the correct way to do OOP but i cant seem to think of a better way to simplify it, and make it reusable (i.e what if we derive a new type)
   it has been suggested to me to do a control break algorithm(seems tough to learn) but i think there should be an easier way, Also suggested to add a member to the classes called   printTotal() but i do not know how to implement that, and I dont know if my Professor wanted it done that way.


thanks for the help
     
         


0
Comment
Question by:imlearning
  • 13
  • 9
22 Comments
 

Author Comment

by:imlearning
Comment Utility
i was also wondering if it would be better to use

char employeeType;

vs

enum EmployeeType {salaried,hourly,piecework};

for reusability

or would this complicate the problem space even more, ie sorting and printing ?
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
I already told you that it would be better if the Employee class and the Employees container don't need to know what kind of derived classes exist. Assume,  you would derive further classes, Yearly,  Season, ...  wouldn't it be great when the printByGroup doesn't need to be changed because of that?

void Employees::printByGroup{
    // declare subtotals accumalators
    // declart totals acummalators
     int lasttype = -1;
     sortByGroup;
     for(i=0;i<EmployeeCount,i++)
     {
         if(all[i].getType() != lasttype) // type changed or very first
         {      
                if (i > 0)
                      all[i-1]->printSubTotal();   // virtual function that prints the sub-total of the previous group
                // print space line

                // print the title of the next group
                all[i]->printTitle();
         }
         //print subtotals for last group
         if (EmployeeCount > 0)
             all[EmployeeCount-1]->printSubTotal();  
 
    }

>>>> char employeeType;   vs  enum EmployeeType {salaried,hourly,piecework};

I would prefer an enum defined in the derived class only:

class Hourly
{
     enum { hourly = 1 };

     ...

     virtual int getType() { return hourly; }
};

Though you would need to care for uniqueness of the types whenever deriving a new class, it makes your derived class independent and the baseclass and the container don't  need to know about the types.


Regards, Alex
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
Correction:

void Employees::printByGroup{
    // declare subtotals accumalators
    // declart totals acummalators
     int lasttype = -1;
     sortByGroup;
     for(i=0;i<EmployeeCount,i++)
     {
         if(all[i].getType() != lasttype) // type changed or very first
         {      
                if (i > 0)
                      all[i-1]->printSubTotal();   // virtual function that prints the sub-total of the previous group
                // print space line

                // print the title of the next group
                all[i]->printTitle();
         }
         // print the current employee
         all[i]->printGross();
         lasttype = all[i]->getType();
     }
    //print subtotals for last group
    if (EmployeeCount > 0)
         all[EmployeeCount-1]->printSubTotal();  
 
 }

0
 

Author Comment

by:imlearning
Comment Utility


If I have this in the base class

virtual void calcGross()=0;

and in my derived class

virtual void calcGross(){this->setGross(salary);}

vs
 
void calcGross(){this->setGross(salary);}

what is the difference they both work and compile , thanks
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> virtual void calcGross()=0;

that defines a *pure* virtual function that *must* be overloaded by any derived class where you want to make instances of.

   Hourly h;   // that doesn't compile if there is no Hourly::calcGross() defined.

A baseclass that has at least one pure virtual function is called an *abstract* class (cause you can't instantiate a concrete type from it).

>>>> what is the difference they both work and compile

The idea is to have different implementation in the derived classes. If all derived classes have the same implementation virtuality makes not much sense. On the contrary, you should implement all common functions at baseclass level not using virtual functions.

Regards, Alex


0
 

Author Comment

by:imlearning
Comment Utility
thanks alex

all[EmployeeCount-1]->printSubTotal();
what would this service print ?  could you should me a sample say for the salary class
 
class Salary : public  Employee{

   private:
      float salary;
      enum{salaried=0};
   public:
      Salary(char * last="",char *first="",float f=0,float deff=0):
         salary(f>0?f:0),Employee(last,first,deff){}
      Salary(Salary &s):
         salary(s.salary),Employee(s.getLastName(),s.getFirstName(),s.getDeffered()){}
     
      void setSalary(const float f){(f<0)?salary=0:salary=f;}
      const float getSalary()const{return salary;}
     
      virtual void calcGross(){this->setGross(salary);}
      virtual int GetType() const { return salaried;}
      void printTitle(FILE* r)
      {
         fprintf(r,"\nSalaried Employees\n"); // header for group
         fprintf(r,"Employee            Salary   Fed         SSI         Net\n");
         fprintf(r,"Name                             State       Deff        Pay\n");
      }
      void printSubTotal(FILE* r){   }
   
};





My Report's spec should be

Group Name
   Group Headers ( i.e Group Name Salary  Taxes       Deffered   Net)
       Employee Line (ie                    salary fed+state  deffered   netPay
    Groups Subtotals
    Groups Averages

Grand Totals All Groups ( gross taxes net..... )



Thanks Alex
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> what would this service print ?  

Would print the subtotal of the last group.

class Salary : public Employee
{
   static int m_groupGross;
   ...
public:
   initGroupTotal() { m_groupGross = 0; }
};

// salary.cpp
#include "salary.h"

int Salary::m_groupGross = 0;

void Salary::calcGross()
{
     setGross(salary);
     m_groupGross += salary;    // add subtotal for each call
}

int Salary::printSubTotal(ostream& os)
{
     os << "------------------------------------------" << endl;
     os << setw(30) << m_groupGross << endl << endl;
     return m_groupGross;
}

>>>> fprintf(r

Use std::ofstream  instead of FILE*.

#include <fstream>
#include <iomanip>


     ofstream ofs("output.txt");
     ofs << ...;    // use it like cout

You can pass it as ostream& cause ofstream is derived from ostream.

Regards, Alex

0
 

Author Comment

by:imlearning
Comment Utility
Alex,

If I go your route then i need to put all accumulators  in the base class and derived classes right


ie

for net

static float groupNet;
static float groupFedTax;
...
void Employee::calcNet(void)
{
 
  if(this->getGross()>0)
  { // protect the data
     this->fedTax = calcFed();
     this->stateTax = calcState();
     this->ssiTax = calcSSI();
     this->net = gross-(fedTax+stateTax+ssiTax+deffered);
     groupNet+=net; // this will total all dervied classes correct?
     groupFedTax+=fedTax;
     
  }
  else
  { cout<<"\nGross is less than Zero";}
}

or should i keep all accumulators in the container report method ?

0
 

Author Comment

by:imlearning
Comment Utility
is printSubTotal() going to be a virtual function

wont i have to cast up to get to that service ?
0
 

Author Comment

by:imlearning
Comment Utility
never mind that last post about the casting up sorry , and i know it should be a virtual


i just need to know where to put all the accumulators

ie the groups FedTax,SSItax,Deffered,Net <-- im thinking dervied class

and AllGroups FedTax,SSItax,deffered,net <--- im thinking base class

0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> If I go your route then i need to put all accumulators in the base class and derived classes right

Yes, that is the weak point of the design above. It would be better if you had additional accumulation classes and pass them to the virtual functions to fill:

struct Total
{
     float net;
     float tax;
};
 
void Employees::printByGroup{
    // declare subtotals accumalators
    // declart totals acummalators
     int lasttype = -1;
     sortByGroup;

     Total  total = { 0 };      // define an acccumaltion object for total
     Total  subtotal = { 0 }; // and one for subtotal of the last group
     // Total single = { 0 };   // you even could define one for a single employee

     for(i=0;i<EmployeeCount,i++)
     {
         if(all[i].getType() != lasttype) // type changed or very first
         {      
                if (i > 0)
                {
                      all[i-1]->printSubTotal(subTotal);    // pass the subTotal to printSubTotal
                      memset(subTotal, 0, sizeof(Total)); // init after print
                }
               
                // print space line

                // print the title of the next group
                all[i]->printTitle();
         }
         // add current to total and subtotal
         all[i]->calcGross(total, subTotal);
         // print the current employee
         all[i]->printGross();
         lasttype = all[i]->getType();
     }
    //print subtotals for last group
    if (EmployeeCount > 0)
         all[EmployeeCount-1]->printSubTotal(subTotal);  

    // last print the total line
    printTotal(total);   // its a Employees member function (or Employee baseclass function)
 
 }


>>>> is printSubTotal() going to be a virtual function

yes, it should be a pure virtual function in Emplyoee that must b e overridden in the derived classes.


   virtual void PrintSubTotal(const Total& subtotal);

>>>> where to put all the accumulators

As just told: best put them to a separate class/struct and pass them with the virtual functions.

Regards, Alex



0
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 

Author Comment

by:imlearning
Comment Utility
ok ill make the stucts , i hope i can do this all today the project is due tomorrow

also
im using visual studio
and as it stands the only way i can make things work is to keep the all the code in the .h files for the classes
ie when i separate .h -> .cpp I can make things link correctly, i don't know if its becuase of my #includes

i have tried at least 10 times to separate them but I always get a linking error

I know this might be asking to much but if I email you the project do you think you could give it a look and see what might be going on ?
 
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> but if I email you the project do you think you could give it a look

The problem is that I am not at home til Thursday. Downloading via mobile phone is a little bit expensive ...

If you post the linker errors I definitively can tell you what is wrong.

>>>> im using visual studio

What version?

Regards, Alex
0
 

Author Comment

by:imlearning
Comment Utility
im using vs 2003.net


lets say i put
#include salary.h
float Salary::m_groupGross = 0;

void Salary::calcGross()
{
     setGross(salary);
     m_groupGross += salary;    // add subtotal for each call
}

in a

salary.cpp file

i get
salary.h(5): error C2504: 'Employee' : base class undefined
salary.cpp(9): error C3861: 'setGross': identifier not found, even with argument-dependent lookup
salary.h(29): error C2061: syntax error : identifier 'FILE'

How about i post all my code to my website and you can have a look ?




0
 

Author Comment

by:imlearning
Comment Utility
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> salary.h(5): error C2504: 'Employee' : base class undefined

in salary.h you need to include employee.h cause the baseclass needs to be fully known.

// salary.h
#ifndef SALARY_H
#define SALARY_H

#include "employee.h"

class Salary : public Employee
{

};


#endif


// salary.cpp

// nothing else beside of STL headers that were needed only in cpp
#include "salary.h"

>>>> float Salary::m_groupGross = 0;

1. You don't need that if you use a accummulator class like Total.

2. All static members must be *declared* within the class scope aand *defined* in cpp file.

Never define variables in header or you'll get duplicate instances if two cpp are including the same header.

>>>> setGross

That was a sample member that still needs to be declared in the class body.  You also simply could set the member by

   m_gross = salary;

Note, if m_gross is a baseclass member you need to set it protected if the derived classes should be able to set it.

>>>> salary.h(29): error C2061: syntax error : identifier 'FILE'

The FILE type is defined in <fstream.h> . I suggested to use ofstream instead. Then you would need to include <fstream> (without .h). In your employee header it is:

#ifndef EMPLOYEE_H
#define EMPLOYEE

#include <iostream>
#include <fstream>
#include <string>


struct Total   // accummulator class
{
   ...  // put here all values that are common to all derived data members

};

class Employee
{
     ...

     virtual void printSubTotal( std::ofstream& ofs, Total& subTotal) = 0;
     ...
};

#endif // EMPLOYEE_H


Note, in the header files don't use the 'using namespace std;' clause but use the std:: prefix instead. In the cpp you may use the clause and can omit std:: then.

Regards, Alex

0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
I am offline for some time. Will try to connect with my notebook later, but I have bad connections some times.

Regards, Alex
0
 

Author Comment

by:imlearning
Comment Utility
yea its required we use stdio.h  so I cant use ofstream
my teacher is a "c" kind of person ...  
 

struct Total   // accummulator class (these are all common ) and will go in the employee class
{
     float deffered;
     float fedTax;
     float ssiTax;
     float stateTax;
     float gross;
     float net;

};

 what about for the derived classes    

i.e

      totalSalary
      totalHours
      totalPayRate
      totalPieces
      totalPricePerPiece

do i make those structs in the derived class?




0
 

Author Comment

by:imlearning
Comment Utility
what do i need to change in my calcGross() to do this


 all[i]->calcGross(total, subTotal);
0
 

Author Comment

by:imlearning
Comment Utility
void Employees::printReport()
{

   FILE * report;//create file
   report= fopen("./Report.lst","wt");//open file
   if (report == NULL)
   {  printf("\n\nReport file open error ....\n");
      exit;
   }
   Total  total = { 0 };// this hold all common      
   this->totalCommonAccumalators(total);
}

 
void Employees::totalCommonAccumalators(Total &t)
{    
   for(int i=0 ; i < EmployeeCount ; i++ )
   {    
      t.deffered+=all[i]->getDeffered();
      t.fedTax+=all[i]->getFedTax();
      t.gross+=all[i]->getGross();
      t.net+=all[i]->getNet();
      t.ssiTax+=all[i]->getSSITax();
      t.stateTax+=all[i]->getStateTax();
           
   }
}

is this ok alex?
0
 

Author Comment

by:imlearning
Comment Utility
Alex.

I got everything done mate ,

thanks for helping me understand




0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 500 total points
Comment Utility
>>>> do i make those structs in the derived class?

Don't know exactly. Normally, I would say that accumulator class only should contain these values that are common to all derived classes thus were suitable to get outputted in a 'subtotal' line of a report in table format. We need the virtual printSubTotal because of the *description* of the group the subtotal was assigned to. But the 'columns' were same for all groups.

Note, if you want derive the accumulators you would need to have a virtual *create* function what seems to me some kind of overkill.

>>>> what do i need to change in my calcGross() to do this: all[i]->calcGross(total, subTotal);

define calcGross in baseclass as

  virtual void calcGross(Total& total, Total& subTotal) = 0;

In the derived classes implement it like

  void Salary::calcGross(Total& total, Total& subTotal)
  {
        // first calculate all single values
        gross = ....;
        // add the calculated values to the totals
        total.gross += gross;
        subtotal.gross += gross;
        ....
  }

It is a different but nearly equivalent approach to that you made with "void Employees::totalCommonAccumalators(Total &t)" . The advantage of my approach is that the subtotals can be handled as well while your function only handles the total.

Regards, Alex



0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now