1

This post is a working example for this Question. However, there are some points that confuse me. That is why I am posting it as another question.

Basically, the problem is how to cast a base class to its subclasses. People have suggested using dynamic memory and newing a fresh instance of the subclass.

I cam up with the following solution, and now wonder how this works. I create an object of the base class (Employee). Set its Id. Then I cast its pointer to a pointer of the subclass and set more members.

// Example program
#include <iostream>
#include <string>
#include <list>
#include <algorithm>
using namespace std;

class Employee {
 public:
  int getId() { return id;}
   void setId( int id) {this->id = id;}
 protected:
  int id;

};

class PartTimeEmployee : public Employee {

};

class FullTimeEmployee : public Employee {
 public:
  int getGrade() {return grade;}
  void setGrade(int grade) {this->grade = grade;}
 private:
  int grade;
};

class Organization {
 public:    
  void addEmployee(Employee* e) { empList.push_back(e); }
  Employee* getEmployee(int id) { 
      for (std::list<Employee*>::iterator it=empList.begin(); it!=empList.end(); ++it) {
          if((*it)->getId() == id) {return (*it);}
      }
      return NULL; 
  }
 private:
  std::list<Employee*> empList;
};

int main()
{
  Employee e1;
  e1.setId(5);

  FullTimeEmployee *pFt1 = (FullTimeEmployee*) &e1; 
  pFt1->setGrade(1);                

  Organization org1;
  org1.addEmployee(pFt1);
  FullTimeEmployee* pFt2 = (FullTimeEmployee*) org1.getEmployee(5);
  cout << pFt2->getId() << endl;
  cout << pFt2->getGrade() << endl;
}

Why does it work? I see it as follows:

Employee: |--id--|... FullTimeEmployee: |--id--|--grade--|...

1- Is this what happens in the background? When I cast to the FullTimeemployee, C++ copies all pre-existing members of the Employee class plus giving you more space based on the size of the new type.

2- Is it safe to use this approach? Should I prefer dynamic memory allocation, e.g. FullTimeEmployee *pFT1 = new FullTimeEmployee(); over this? If yes why? why can't I just go with pointer casting?

UPDATE: What makes this example different from casting to void*? You just play with memory addresses. What can go wrong as long as you know how much memory you need?

Community
  • 1
  • 1
towi_parallelism
  • 1,421
  • 1
  • 16
  • 38
  • 2
    "Why does it work" - it doesn't. You were only fooled into believing it did. An `Employee` is not a `FullTimeEmployee`. The moment you claimed it was (third line of `main()`) the train left the track and hurtled into the abyss. – WhozCraig May 29 '15 at 09:26
  • @WhozCraig: How is it different from casting to void*?, what is wrong with it if you know how much memory your new type would occupy? – towi_parallelism May 29 '15 at 10:47
  • 1
    You're violating a number of rules in the language, and in so doing invoking undefined behavior. I honestly can't put it any simpler than that. Cast to `void*` all you want, that *still* doesn't make an `Employee` a `FullTimeEmployee` – WhozCraig May 29 '15 at 10:53

1 Answers1

2

It only seems to work, because e1 is not an instance of any of the child classes, leading to undefined behavior.

To make it work, you need to use dynamic memory allocations:

Emplyoee *e1 = new FullTimeEmployee;

Now it works with the casting, because e1 actually points to a FullTimeEmployee object.

But you should really use static_cast, like

FullTimeEmployee *pFt1 = static_cast<FullTimeEmployee*>(e1);
pFt1->setGrade(1);

Or just

static_cast<FullTimeEmployee*>(e1)->setGrade(1);

Also you should read about downcasting, which is what you are doing.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • `Employee* pFt1 = new FullTimeEmployee(); pFt1->setGrade(1); ` In function 'int main()': 49:9: error: 'class Employee' has no member named 'setGrade' – towi_parallelism May 29 '15 at 09:32
  • @TOWI_Parallelism Read my *whole* answer please. – Some programmer dude May 29 '15 at 09:34
  • Alright. I got the new edit now. But, that's exactly what I asked. So, you think I must go for the dynamic memory approach. There is no way to do this on the stack. right? Then I will need a virtual destructor and everything. right? – towi_parallelism May 29 '15 at 09:37
  • @TOWI_Parallelism The only way to do it on the stack is if you declare `e1` to *be* an `FullTimeEmployee` object instance. And yes virtual destructor would be a good idea. – Some programmer dude May 29 '15 at 10:14
  • I am still having a problem. At the end of they day, they are all addresses. What prevents you from doing what I did? I mean it is like saying never cast to void*, because you don't know the size of the memory it points to. – towi_parallelism May 29 '15 at 10:44
  • @TOWI_Parallelism But if you do like you do in the question, the variable `e1` is an instance of `Employee`, and have only the `id` member. When you write to the `grade` field using the `FullTimeEmployee` pointer, where do you write then? The object `e1` doesn't *have* a `grade` member. – Some programmer dude May 29 '15 at 10:50
  • I don't do that. I say now this address points to an object which needs more memory and includes a grade member, by: `FullTimeEmployee *pFt1 = (FullTimeEmployee*) &e1; ` – towi_parallelism May 29 '15 at 10:53