Solved

Casting from derived class

Posted on 2006-11-20
20
423 Views
Last Modified: 2012-06-27
I have a base Employee class and three derived class , Salary, Hourly, and PieceWork

I have a container class
class Employees{
 
   private:
      Employee *all[MAX];
      int EmployeeCount;
      void qs_lname(int left, int right);
   public:
      Employees():EmployeeCount(0){}
      void add(Employee &e);
      void remove(Employee &e);
      int search(char* l);
      void quickSort(){qs_lname(0,EmployeeCount-1);}
      void calcAllGross(void);
      void calcAllNet(void);
      void printByGroups(void);
     
};

so im working on printByGroups which prints a report grouped by the derived types

and I need to do this

subTotalHours=all[0]->getHours(); // but getHours is a member of the derived Hourly class
   error C2039: 'getHours' : is not a member of 'Employee'

how do I cast so that i can get to that method
0
Comment
Question by:imlearning
  • 10
  • 5
  • 3
  • +1
20 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 17984817
Since you know that only the derived classes have that method, cast the pointer accordingly, e.g.

subTotalHours=((Hourly*)all[0])->getHours(); // but getHours is a member of the derived Hourly class
 

0
 

Author Comment

by:imlearning
ID: 17984860
hi jkr

i tried doing just that
float subTotalHours=((Hourly*)all[0])->getHours();

and i get
error C2059: syntax error : ')'
syntax error : identifier 'Hourly'
error C2061: syntax error : identifier 'Hourly'
error C2065: 'Hourly' : undeclared identifier

Let me explain how I have my project setup

I have employee.h // Has Employee(base)class  and Employees (container) Class
and hourly.h // #include "employee.h"
      salary.h// #include "employee.h"
      pieceWork.h // #include "employee.h"
and my payroll.cpp (main)
     #include "employee.h"
     #include "hourly.h"
     #include "salary.h"
     #include "piecework.h"
is that the problem ? ( im new with header files)

thanks

0
 

Author Comment

by:imlearning
ID: 17984868
also here is my main

   Employee *test = new Hourly("Doe","John",27.00,46.50,50);
   Employee *test2 = new PieceWork("Whittle","Ed",11.50,25.50,0);
   Employee *test3 = new PieceWork("Marion","Louise",13,40,100);
   Employee *test4 = new Salary("Prentiss","Paula",15.75*50.5,124);
   Employee *test5 = new Hourly("Davidson","Carl",8.75,38,15);
   
   Employees Company;

   Company.add(*test);
   Company.add(*test2);
   Company.add(*test3);
   Company.add(*test4);
   Company.add(*test5);
0
 
LVL 86

Expert Comment

by:jkr
ID: 17985063
No, that should work. It is however hard to diagnose what'S going wrong without seeing more of the code.
0
 
LVL 2

Expert Comment

by:numansiddique
ID: 17985087
You add the function getHours(); in the Employee class.
for example
class Employee
{
..
..
..
public:
int getHours()
{
}
};

0
 

Author Comment

by:imlearning
ID: 17985094
heres my code, thanksfor your time jkr
=======================================================
class Employee {
  protected:
     enum EmployeeType {salaried,hourly,piecework}; // derived types
  private:
     char *lname;
     char *fname;
     float deffered;
     float gross;
     float net;
     float fedTax;
     float stateTax;
     float ssiTax;
     float calcFed(void) {return (gross-deffered)*FEDTAXRATE;}
     float calcState(void){return (fedTax * STATETAXRATE);}
     float calcSSI(void){return (gross-deffered)*SSITAXRATE;}
  public:
     Employee(const char*last="",const char* first="",float deff=0.0)
     :lname(NULL), fname(NULL),deffered(deff),gross(0), net(0), fedTax(0), stateTax(0), ssiTax(0)
     {
        int len = strlen(last);
        lname = new char[len+1];
        strcpy(lname, last);
        len = strlen(first);
        fname = new char[len+1];
        strcpy(fname, first);

      }
     ~Employee()//destruct
     {
        delete [] fname;
        delete [] lname;
     }
     char* getLastName(void)const{return this->lname;}
     char* getFirstName(void)const{return fname;}
     const float getDeffered(void)const{return deffered;}
     const float getGross(void)const{return gross;}
     const float getNet(void)const{return net;}
     const float getFedTax(void)const{return fedTax;}
     const float getStateTax(void)const{return stateTax;}
     const float getSSITax(void)const{return ssiTax;}
     
     void setLastName(const char * last){ int len =strlen(last);lname = new char[len+1];strcpy(lname, last);}
     void setFirstName(const char * first){int len =strlen(first);fname = new char[len+1];strcpy(fname, first);}
     void setDeffered(const float def){deffered=def;}
     void setGross(const float f){gross=f;}
     void calcNet(void);
 
     virtual void calcGross(void)=0;
     virtual EmployeeType GetType() const = 0;
   
};
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);
  }
  else
  { cout<<"\nGross is less than Zero";}
}
===================================================
class Employees{
 
   private:
      Employee *all[MAX];
      int EmployeeCount;
      void qs_lname(int left, int right);
   public:
      Employees():EmployeeCount(0){}
      void add(Employee &e);
      void remove(Employee &e);
      int search(char* l);
      void quickSort(){qs_lname(0,EmployeeCount-1);}
      void calcAllGross(void);
      void calcAllNet(void);
      void printByGroups(void);
     
};

void Employees::calcAllGross(void)
{
   for(int i =0;i<EmployeeCount;i++)
      all[i]->calcGross();
}


void Employees::qs_lname(int left, int right)
{
   int i, j;
   const char *x; // temp string
   i = left; j = right;
   x = all[(left+right)/2]->getLastName(); // sorting by last name
   do
   {
      while((strcmp(all[i]->getLastName(),x) < 0) && (i < right))i++;
      while((strcmp(all[j]->getLastName(),x) > 0) && (j > left)) j--;
      if(i <= j)
      { // swap
         Employee *temp =all[i];
         all[i] = all[j];
         all[j] = temp;
         i++; j--;
     }
   } while(i <= j);
   if(left < j) qs_lname(left, j);
   if(i < right) qs_lname(i, right);
}
void Employees::printByGroups(void)
{

   float subTotalHours=((Hourly*)all[0])->getHours();
   

}


void Employees:: calcAllNet(void)
{
   for(int i =0;i<EmployeeCount;i++)
      all[i]->calcNet();

}
void Employees::add(Employee &e)
{
   if(EmployeeCount<MAX)
   {  int found=search(e.getFirstName()); // search first if employee exists
      if(found==-1)
      {
         all[EmployeeCount]=&e;
         EmployeeCount++;
         return;
       }
       else
       {
          cout<<"\nEmployee Already Exist: not added: \n";
          return;
       }
    }
    else
       cout<<"\n Error :Container Is Full:\n";
}
int Employees::search(char* l)
{
   for(int i=0;i<EmployeeCount;i++)
   {
      if(strcmp(l, all[i]->getLastName())==0)
         return i;
   }
   return-1;
}
void Employees::remove(Employee &e)
{
   int found=search(e.getLastName());
   if (found >= 0)
   {   Employee *erase=0; // Null Pointer
       all[found]=all[EmployeeCount-1];//set found to last pointer
       all[EmployeeCount-1]=erase;//set last pointer to null
       --EmployeeCount;
       quickSort();          
   }
}
==============================================
class Hourly : public  Employee{

   private:
      float hours;
      float payrate;
   public:
      Hourly(char * last="",char *first="",float pr=0,float hr=0,float deff=0):
         hours(hr>0?hr:0),payrate(pr>0?pr:0),Employee(last,first,deff){}
      Hourly(Hourly &h):
         hours(h.getHours()),payrate(h.getPayRate()),Employee(h.getLastName(),h.getFirstName(),h.getDeffered()){}

      void setHours(const float f){(f>0)?hours=f:hours=0;}
      float getHours(void)const{return hours;}
      void setPayRate(const float f){(f>0)?payrate=f:payrate=0;}
      float getPayRate(void)const{return payrate;}

      void calcGross()
      {
         if(hours <= 40)
          this->setGross(hours * payrate);
         else
          this->setGross( 40 * payrate + (hours-40)* 1.5 * payrate);
      }
      virtual EmployeeType GetType() const { return hourly;}
};
================================================
0
 
LVL 2

Expert Comment

by:numansiddique
ID: 17985096
If you have the pointer of a parent class, then you cannot access the functions of the derived class. If all the derived classes are having getHours function, then declare the function in parent class ie Employee class as i have shown above and implement the function in all the derived classes. the compiler will call the function of the class to which the object is pointing at run time.
The error is because you have Employee class pointer ie test,test2, test3, test4 . when you call test->getHours() compiler cannot resolve the function name because its not defined in the Employee class.

hope this could help you
0
 
LVL 86

Expert Comment

by:jkr
ID: 17985122
What file is

void Employees::printByGroups(void)
{

   float subTotalHours=((Hourly*)all[0])->getHours();
   

}

?

You will have to add

#include "hourly.h"

to that one also.
0
 

Author Comment

by:imlearning
ID: 17985342
per the assignment spec  getHours can't be a member of Employee


I tried adding #include"hourly.h" to "employee.h"
error C2440: 'initializing' : cannot convert from 'Hourly *' to 'Employee *'
error C2440: 'initializing' : cannot convert from 'Hourly *' to 'Employee *'
error C2501: 'Hourly::EmployeeType' : missing storage-class or type specifiers
hourly.h(2): error C2504: 'Employee' : base class undefined

Should I put all the derived classes and base class in one file ?
Or should i change my container data member ?? or is that not the problem ?

thanks guys
0
 

Author Comment

by:imlearning
ID: 17985354
I also tried putting all the code into one cpp file and i get
test.cpp(139): error C2065: 'Hourly' : undeclared identifier
test.cpp(259): error C2061: syntax error : identifier 'Hourly'


is it because of the way my container is holding the data ?
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.

 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 17985664
Put the classes/implementation to the following files:

// employee.h
#ifndef EMPLOYEE_H
#define EMPLOYEE_H

class Employee
{
    ...
      friend class Employees;  
};

#endif
// end of employee.h

// employees.h
#ifndef EMPLOYEES_H
#define EMPLOYEES_H

class Employees
{
    ...
};

#endif
// end of employees.h


// hourly.h
#ifndef HOURLY_H
#define HOURLY_H

#include "employee.h"

class Hourly : public Employee
{
    ...
};
#endif
// end of hourly.h


put all your member implementation to according employee.cpp, employees.cpp, hourly.cpp .

All cpp can include all needed header files.

Some remarks to the class design:

if you are not allowed to declare friend classes, you have to provide public get/set functions for class Employee.

Hourly doesn't seem a good derivation of class Employee. A public derivation is a so-called "IS A" relation, i.e. an 'Hourly' *is an* 'Employee' in your case. That hardly is a good design. If I am right that is the reason why you got problems when managing 'Hourly' objects by means of an array of employee pointers. I would say class Employee should 'have' an Hourly object as member - or use a pointer if not all employees have a 'Hourly' extension.

If you post the spec we can give you more advice.

Regards, Alex
0
 

Author Comment

by:imlearning
ID: 17987450
Hi alex,
I dont know if you remember  but you helped me build the Employee and Employees class. And now our assignment was to derive three classes(salary,hourly,piecework) from the Employee class, and make the Employee class abstract by making calcGross a purevirtual service.

So since i had to do this
Employee *test = new Hourly("Doe","John",27.00,46.50,50);
I had to change my container(Employees class) right ? becuase the old one did this

Employees(void) : allEmployees(NULL), EmployeeCount(0), EmployeeSize(0)
     {allEmployees= new Employee[EmployeeSize];}
 // and i was getting a compile error saying i cant instance a abstract type

So I changed my container to hold an array of pointers to Employees only,
   Employee *all[MAX];<--is it ok that i dont dynamically allocate w/ new
   int EmployeeCount;
public:
    Employees():EmployeeCount(0){} <-is it ok that i dont dynamically allocate w/new

Was that the right thing to do ?

I will work on creating the header files in the meantime, it doesnt help that this project is due today(5:30pm pacific time), He only gave us a week to do it, but i thought it would be a cake walk since i got 110% on the last project you helped me with

thanks alex
ps for reference my old ID was dreaminbinay
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 17987759
>>>> And now our assignment was to derive three classes(salary,hourly,piecework)

As I told you above, a public derivation is called an "is a" relation. So, hourly, salary, piecework are very poor names for derivations of class employee when looking on them with an OO kind of view... But never mind. The above at least gets some sense...

>>>> So I changed my container to hold an array of pointers to Employees only,

Yes, that is good. You need access the baseclass pointers virtually to get access to the *real* objects.

>>>> is it ok that i dont dynamically allocate w/ new

That depends on your requirements. If you can determine a suitable maximum size of the number of employees, it is ok. If your requirement is to be able to manage a dynamical count of employees you should turn to the Employees class we already made dynamically last time.

>>>> per the assignment spec  getHours can't be a member of Employee

That is ok, cause you have three cases of employees, you shouldn't have a virtual function in the baseclass that is only valid for one specialization. The key issue is that you should *drive* the application by iterating the employees array and call the virtual function calcGross. After calling you are in the context of the derived class waht means that in Hourly::calcGross you can call Hourly::getHourly(...)  without problems and in Salary::calcGross you can call Salary::getMonthlySalary(...), and so on... Do you get the point?

Regards, Alex


0
 

Author Comment

by:imlearning
ID: 17988327
alex
-- I set up the header files per your spec

--when im calcingGross i do that in the container
void Employees::calcAllGross(void)
{
   for(int i =0;i<EmployeeCount;i++)
      all[i]->calcGross();
}
and that works fine


now im lost again....

per the assignment spec we have to produce a report method in the Employees class that totals and subtotals the derived classes.

so  making this method
void Employees::printByGroups(void)
{  

   for(int i=0 ; i<EmployeeCount ; i++)
   {
      if(all[i]->GetType()==1) //this is a hourly derived type
      {  //print employee line to file and subTotals for Hourly go here
         float subTotalHours=((Hourly*)all[0])->getHours(); <-- trying to cast up
      }
   }
}


is this the wrong approach ? thanks again for your time
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 17988803
>>>> if(all[i]->GetType()==1)

That is bad. Or let us say it is against virtuality that the baseclass or its container do know about all types (so I must admit that most report writers know about the kind of objects supposed to get printed).

To make it really good, you should sort the container on the type attribute. I think a bubble sort would do like

void Employees::sortTypes()
{
       for(int i=0 ; i<EmployeeCount ; i++)
         for(int j=i+1 ; j<EmployeeCount ; j++)
         {
               if(all[j]->GetType() < all[s]->GetType() ) //this is a hourly derived type
               {
                   Employee* pTemp = all[i];
                   all[i] = all[j];
                   all[j] = pTemp;
               }
        }
}

After doing so, the container was separated into groups.

You now can iterate again and if type changes you call a virtuelly defined 'printTotal' function to close the previous group (of course only if it isn't the very first group). The printTotal would print static defined members of the derived class which represent the total of all 'gross' values for that class. You have to fill these values in calcGross function. Then call 'printTitle' virtually what would print the title line(s) for the next group. For each element of that group call virtually 'printGros' which prints the statistics for one element of the container. Finally, call 'printTotal' for the last group (outside of the loop.)

void Employees::printReport()
{
     // check (EmployeeCount > 0)
     ...    
     // sort employees into groups
     ...
     // init last type to an unknown type, e. g. -1
     ...
     
     // iterate the sorted container
     for( ...
     {
         // check if type changed
         ...
         {
              // call PrintTotal for the previous element  (if exist)
              ...
              // call PrintTitle for the current element
              ...              
              // save last type to current type
              ...
         }
         // call PrintGross for the current element
     }
     // call PrintTotal for the last element in the container
     ...
}

Hope, it was understandable.

Regards, Alex
0
 

Author Comment

by:imlearning
ID: 17989060
Alex I understand the sortType

so here is what my driver is doing

   Employee *test = new Hourly("Doe","John",27.00,46.50,50);
   Employee *test2 = new PieceWork("Whittle","Ed",11.50,25.50,0);
   Employee *test3 = new PieceWork("Marion","Louise",13,40,100);
   Employee *test4 = new Salary("Prentiss","Paula",15.75*50.5,124);
   Employee *test5 = new Hourly("Davidson","Carl",8.75,38,15);
   
   Employees Company;

   Company.add(*test);
   Company.add(*test2);
   Company.add(*test3);
   Company.add(*test4);
   Company.add(*test5);

   Company.calcAllGross();
   Company.calcAllNet();
   Company.sortTypes();

so now all of my pointers are sorted by types

now for the Employees::printReport()
my Report headers are this

Employee       Pay        Reg Hrs             Gross           Fed               SSI             Net
Name            Rate       Ovt Hrs              Pay             State             Defr             Pay
=======  ======     ======      ======       ======      ======       =====

and per assignment spec I need

1.) Each derived types to be grouped, and sorted by last name, and have subtotals for each group i.e subtotal all Hourly Employees  payRate, regHours,OvtHours ....

so im going to change the sortType to include sorting by last name

now when i get to here im not understanding the logic
void Employees::printReport()
{
     if (EmployeeCount > 0)
     ...    
     this.sorthType();
     ...
     // init last type to an unknown type, e. g. -1 <--- confused??
     ...
     
     for( ...
     {
         // check if type changed <--- how do i do this??
         ...
         {
              // call PrintTotal for the previous element  (if exist) <-- what is printTotal ?
              ...
              // call PrintTitle for the current element
              ...              
              // save last type to current type
              ...
         }
         // call PrintGross for the current element
     }
     // call PrintTotal for the last element in the container
     ...
}
thanks alex
0
 

Author Comment

by:imlearning
ID: 17989139
also alex can you take a look at my add

void Employees::add(Employee &e)
{
   if(EmployeeCount<MAX)
   {  int found=search(e.getFirstName()); // search first if employee exists
      if(found==-1)
      {
         all[EmployeeCount]=&e; <<----- is this ok ????
         EmployeeCount++;
         return;
       }
       else
       {
          cout<<"\nEmployee Already Exist: not added: \n";
          return;
       }
    }
    else
       cout<<"\n Error :Container Is Full:\n";
}
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 17989215
>>>> so im going to change the sortType to include sorting by last name

Change the sorting condition to

              if(all[j]->GetType() < all[i]->GetType()  ||
                  (all[j]->GetType() == all[i]->GetType() && all[j]->GetName() < all[i]->GetName()  ) )

// init last type to an unknown type, e. g. -1 <--- confused??

e. g.

// employee.h

enum { no_type = -1, hourly, salary, piecework };


// employees.cpp

   ...
   int lasttype = no_type;   // that is done to get the title of the first group printed

>>>> // check if type changed <--- how do i do this??

Exactly, that is the corresponding part to the above:

     if (all[i]->getType() != lasttype)
     {
           all[i]->PrintTitle();   // pints the title of any new group including the very first
           ...

>>>> <-- what is printTotal ?

You are right it should be called PrintSubTotal. It is the sum line (the total for Gross Pay and Net Pay) for one group.

Regards, Alex

0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 500 total points
ID: 17989455
>>>>         all[EmployeeCount]=&e; <<----- is this ok ????

hmmm...

No. When you changed the container to storing pointers instead of objects (copies) you should have changed the interfaces as well. That means you should pass pointers and return pointers. The main reason for changing the return type to pointer value is that you can't assume for certain that the container always is non-empty. If it is empty you have to return a NULL pointer (and have to check for NULL when calling). Passing a reference and storing a pointer also is bad design. Normally, a container that stores copies of the objects is more robust (and therefore better). However, when storing elements of different derived classes by means of baseclass addresses YOU HAVE to store pointers cause baseclass references can't be stored. You could make a copy of the element passed in by calling an appropriate virtual copy function. Then, you could store the pointers you created in the container class what is better then using the pointers that were created outside.  

   Hourly test1("Doe","John",27.00,46.50,50);
   allEmployees.add(test1);

   ...
   void Employees::add(const Employee& emp)
   {
         Employee* pCopy = emp.createExactCopy();  // that is a virtual function overloaded in all derived classes
                                                                           // e. g. by  E
   }

createExactCopy is a virtual function overloaded in all derived classes, e. g by
                                                                         

   Employee* Hourly::createExactCopy() { return new Hourly(*this);  // use copy constructor }


With that you could delete all pointers stored in the container in the destructor of Employees

Employees::~Employees()
{
     for (int i = 0; i < EmployeeCount; ++i)
           delete all[i];
}

Note, you need virtual destructors for all classes when deleting them via baseclass pointer.

So, class Employees would create the pointers and delete the pointers what is the way it should be done. If you create the pointers in main() and delete them in ~Employees, it is bad design. If creating pointers in main() you should delete them in main() (and pass them as pointers).  Or you would do

int main()
{
  Hourly test1("Doe","John",27.00,46.50,50);
  allEmployees.add(&test1);  
  ...


}

but the 'create copy method' is the best of all alternatives.

Regards, Alex

P.S. Unfortunately, I have to go offline for some hours - I don't know whether I can get online with my notebook later. So, if you have urgent questions you should post them within the next minutes.


0
 

Author Comment

by:imlearning
ID: 17989521
Hey thanks alot alex ..

i got everything .. alot to take in for a newbie , but as i have said before you have taught me more in one of these post than ive learned all semester.

thanks again for your time and detailed posts


0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Templates For Beginners Or How To Encourage The Compiler To Work For You Introduction This tutorial is targeted at the reader who is, perhaps, familiar with the basics of C++ but would prefer a little slower introduction to the more ad…
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

707 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

22 Experts available now in Live!

Get 1:1 Help Now