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\$ – Martin York Apr 14 at 17:40
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\$ – Richard Neumann Apr 14 at 21:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.