Roxox
Roxox

Reputation: 99

Why my overloaded operator is not working for derived class

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

Answers (2)

Turtlefight
Turtlefight

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

Vlad from Moscow
Vlad from Moscow

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

Related Questions