Reputation: 99
I was trying to learn operator overloading in C++ PL. I made an exercise shown below. What I want to do is overload << operator for each derived class and use it on my main. But whenever I do it, its only working for Base class. What is the problem here?
Class Employee:
class Employee {
public:
string name;
int id;
int exp_level;
double salary;
Employee() {
this->name = "";
this->id = 0;
this->exp_level = 0;
this->salary = 0.0;
}
~Employee() {
//WTF
}
virtual void calculateSalary() {
//CODE
}
virtual void registerX() {
//CODE
}
friend ostream& operator<<(ostream& os, const Employee& e) {
os << e.name << " " << e.exp_level << " " << e.id << " " << e.salary << endl;
return os;
}
};
Class Technical:
class Technical : public Employee {
public:
string profession;
Technical() {
this->profession = "";
}
~Technical() {
}
virtual void calculateSalary() {
//CODE
}
virtual void registerX() {
//CODE
}
friend ostream& operator<<(ostream& os, const Technical& e) {
os << e.name << " " << e.exp_level << " " << e.id << " " << e.salary << "Technical" << endl;
return os;
}
};
Class Engineer:
class Engineer : public Employee {
public:
Engineer() {
}
~Engineer() {
}
virtual void calculateSalary() {
//CODE
}
virtual void registerX() {
//CODE
}
friend ostream& operator<<(ostream& os, const Engineer& e) {
os << e.name << " " << e.exp_level << " " << e.id << " " << e.salary << "Engineer" << endl;
return os;
}
};
Main Method:
int main()
{
Employee* e = new Employee();
Employee* t = new Technical();
Employee* ee = new Engineer();
cout << *e << endl;
cout << *t << endl;
cout << *ee << endl;
}
Output:
0 0 0
0 0 0
0 0 0
Upvotes: 0
Views: 1196
Reputation: 10975
C++ chooses the best overload based on the static type's of the function arguments, and because the type is Employee
in this case, the Employee
's operator<<
gets called.
If you want it to call the correct version when you have a static type pointer / reference to it that doesn't match it's dynamic type you'll have to use a virtual function or use dynamic_casts
/ typeid
to check for the concrete runtime type (virtual functions are the cleanest approach imho)
Example: godbolt
class Employee {
public:
virtual ~Employee() = default;
friend std::ostream& operator<<(std::ostream& os, const Employee& e) {
return e.put(os);
}
protected:
virtual std::ostream& put(std::ostream& os) const {
os << "Employee!";
return os;
}
};
class Technical : public Employee {
protected:
std::ostream& put(std::ostream& os) const override {
os << "Technical Employee!";
return os;
}
};
class Engineer : public Employee {
protected:
std::ostream& put(std::ostream& os) const override {
os << "Engineer Employee!";
return os;
}
};
int main() {
Employee* e = new Employee();
Employee* t = new Technical();
Employee* ee = new Engineer();
std::cout << *e << std::endl;
std::cout << *t << std::endl;
std::cout << *ee << std::endl;
delete ee;
delete t;
delete e;
}
would result in:
Employee!
Technical Employee!
Engineer Employee!
Also keep in mind that once you have at least 1 virtual function in a class then the destructor should almost definitely be also virtual.
e.g.:
Employee* e = new Technical();
delete e;
would only call ~Employee()
, but not ~Technical()
, if the destructor is not virtual.
So whenever you want to delete an object through a pointer to one of it's base-classes the destructor needs to be virtual, otherwise it's undefined behavior.
Upvotes: 3
Reputation: 311088
Due to these declarations
Employee* e = new Employee();
Employee* t = new Technical();
Employee* ee = new Engineer();
the static type of the expressions *e
, *t
, *ee
is Employee &
. So the operator
friend ostream& operator<<(ostream& os, const Employee& e)
is called for all three objects.
A simple way to make the friend operator << "virtual" is to define in each class a virtual function like for example
virtual std::ostream & out( std::ostream & ) const;
and define the (only) friend operator like
friend ostream& operator<<(ostream& os, const Employee& e)
{
return e.out( os );
}
The virtual function out
need to be redefined in each derived class.
Upvotes: 3