Why my overloaded operator is not working for derived class

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.


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.