Short answer: yes.
Long answer: maybe, but probably yes in your case.
The primary point of polymorphism is to make that kind of basic logic of asking, "What type of employee is this? Oh, it's a part-time employee? Do the right thing for part-time employees" an automatic, rather than manual process. Inheritance and virtual functions are one means of doing this automatically.
This can become a very useful concept to reduce maintenance overhead, as you can now add new employee types to your heart's content without constantly writing code checking what type of employee you are working with everywhere. You end up turning what would normally be a inextensible solution that would require you to manually extend the range of types you support in potentially many places intrusively in your codebase into something you can extend to the side non-intrusively without touching anything you've written so far.
The abstraction also helps you identify a common denominator interface that all types share. To think abstractly is generally focusing on what things should all do, not specific details of what they are. Focusing on that helps you concentrate on what things should be doing, and will give your codebase a greater degree of flexibility towards evolving changes as you can swap out what things are without breaking the code specifying what they should do.
A basic analogy I like to use is that if you wrote a boatload of code telling a robot to walk to various places, swapping the legs of that robot with wheels would break all of that code and you would have to rewrite it all. If, instead, the code is more abstract and telling the robot to merely go to various places, you can swap the robot's legs with a warp drive, jetpacks, and molecular teleporters and all of the code you painstakingly wrote would continue to work. A lot of the focus of software engineering is in the science (and perhaps art) of creating maintainable codebases. A large part of that is making the cost of changes cheaper, and for that you want more of these solutions which automatically adapt with your changes, and fewer of those which you have to manually adapt against your changes.
So on to your question:
first of all, is it safe to return a pointer? I assume that its
address is permanent, because it will send the address of that element
in the empList. right?
Pointers are perfectly fine here. Most of the dangers of using raw pointers are associated with memory management (ownership), not access. If your organization class is the one dealing with the memory management responsibility and Employee*
is just used a handle to access a specific employee, it's perfectly fine. Using things like shared_ptr
here would be overkill unless you need that kind of shared ownership.
When I cast Employee* to a FullTimeEmployee*, obviuosly I lose the
grade field. How can I resolve this?
You generally either want the notion of a 'grade' to be applicable to all employees with either a virtual function or a non-virtual function and actual storage of the grade in your abstract Employee
base class. Remember that polymorphism is about designing a public interface that all such employees would have in common regardless of whether they're part-time or full-time, regardless of their specific specialty, etc.
Now about your code:
std::list<Employee> empList;
This is a problem. You don't want to store Employee
like this by value or else it's going to slice away the data required for a specific employee subclass. It needs to be Employee*
or better, something like a shared_ptr
or vector<unique_ptr<Employee>>
to which you assign the memory address for specific employee subtype objects like new PartTimeEmployee
.
We also need a virtual destructor in Employee
so that when you or a smart pointer try to call delete
through Employee*
, it triggers the correct destruction logic for the specific type of employee we're pointing to like so:
class Employee {
public:
virtual ~Employee() {}
...
};
If I try to explain why informally, it's because the system doesn't know what Employee* is pointing to. So if you try to destroy that, it won't know what to destroy specifically unless there's a virtual destructor which tells it that the destructor logic can be different for different employee subtypes.