0
\$\begingroup\$

I have the following working code:

person.h

#ifndef PERSON_H
#define PERSON_H

#include <iostream>
#include <string>

class Person {
private:
    std::string firstName;
    std::string middleName;
    std::string lastName;

public:
    Person() = default;
    Person (std::string pFirstName, std::string pMiddleName, std::string pLastName);
    Person (std::string pFirstName, std::string pLastName);

    std::string getFirstName();
    void setFirstName(std::string pFirstName);
    std::string getLastName() const;
    void setLastName(std::string pLastName);
    std::string toString() const;

    friend std::ostream& operator<<(std::ostream& target, Person const & person);
};

#endif

person.cpp

#ifndef __person__
#define __person__

#include <iostream>
#include <string>

#include "person.h"

using std::string;
using std::ostream;

Person::Person (string pFirstName, string pMiddleName, string pLastName)
: firstName(pFirstName), middleName(pMiddleName), lastName(pLastName)
{
}

Person::Person (string pFirstName, string pLastName)
: firstName(pFirstName), middleName(""), lastName(pLastName)
{
}

string Person::getFirstName ()
{
    return firstName;
}

void Person::setFirstName (string pFirstName)
{
    firstName = pFirstName;
}

string Person::getLastName () const
{
    return lastName;
}

void Person::setLastName (string pLastName)
{
    lastName = pLastName;
}

string Person::toString () const
{
    if (middleName == "")
        return firstName + " " + lastName;

    return firstName + " " + middleName + " " + lastName;
}

ostream& operator<< (ostream& target, Person const & source)
{
    target << source.toString();
    return target;
}

#endif

tweeter.h

#ifndef TWEETER_H
#define TWEETER_H

#include <string>

#include "person.h"

class Tweeter
: public Person
{
private:
    static int gid;
    int id;
    std::string twitterHandle;

public:
    Tweeter(std::string pFirstName, std::string pMiddleName, std::string pLastName, std::string pTwitterHandle);
    Tweeter(std::string pFirstName, std::string pLastName, std::string pTwitterHandle);
    std::string toString() const;
    friend std::ostream& operator<< (std::ostream& target, Tweeter const & source);
};

#endif

tweeter.cpp

#ifndef __tweeter__
#define __tweeter__

#include <iostream>

#include "tweeter.h"

using std::ostream;
using std::string;
using std::to_string;

int Tweeter::gid = 0;

Tweeter::Tweeter(string pFirstName, string pMiddleName, string pLastName, string pTwitterHandle)
: Person(pFirstName, pMiddleName, pLastName), id(gid), twitterHandle(pTwitterHandle)
{
    gid++;
}

Tweeter::Tweeter(string pFirstName, string pLastName, string pTwitterHandle)
: Tweeter(pFirstName, "", pLastName, pTwitterHandle)
{
}

string Tweeter::toString() const
{
    return "[" + to_string(id) + "] " + Person::toString() + " (" + twitterHandle + ")";
}

ostream& operator<< (ostream& target, Tweeter const & source)
{
    target << source.toString();
    return target;
}

#endif

As you can see, I have a nearly duplicate implementation for the << operator in the subclass Tweeter. I had to do that, since, if I just use the << operator implementation inherited from Person, C++ would silently cast the Tweeter instance as Person as per the operator function's signature and call Person::tostring() instead of Tweeter::toString(). Is it possible to just have one implementation of the << operator that can be inherited without override and thusly code duplication?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Yes. Make operator<< call a virtual function on the object. Then you only need to define it for person (not for Tweeter). This will make it print the most derived version. \$\endgroup\$ Commented Apr 14, 2021 at 17:40

1 Answer 1

1
\$\begingroup\$

Yes use a virtual print() function.

PS. I would also make the toString use the virtual print function to make it more effecient.

class Person 
{
    // OTHER STUFF

    virtual void print(std::ostream& target = std::cout) const
    {
        target << firstName << " ";
        if (middleName != "") {
            target << middleName << " ";
        } 
        target << lastName;
    }
    std::string toString() const
    {
        std::stringstream  stream;
        this->print(str);
        return stream.str();
    }

    friend std::ostream& operator<<(std::ostream& target, Person const& person)
    {
        person.print(target);
        return target;
    }
};


class Tweeter: public Person
{
    // OTHER STUFF


    // Don't need this any more: => std::string toString() const;
    // Don't need this any more: => friend std::ostream& operator<< (std::ostream& target, Tweeter const & source);

    virtual void print(std::ostream& target = std::cout) const override
    {
         target << "[" << to_string(id) << "] ";
         Person::print(target);
         target <<  " (" << twitterHandle << ")";
    }
};
\$\endgroup\$
1
  • \$\begingroup\$ Thanks. Since toString() was only a helper for the << I actually don't need it anymore after introducing the virtual print() functions. \$\endgroup\$ Commented Apr 14, 2021 at 21:56

You must log in to answer this question.