Output of User-Defined Types
Imagine you have written a class Person:
//
// Person.h
// iostreams - User-defined types
//
// Created by Bryan Higgs on 9/4/24.
//
#ifndef Person_h
#define Person_h
#include <iostream>
class Person
{
public:
// Constructor
Person(const std::string &nm,
const std::string &street = "",
const std::string &town = "",
const std::string &state = "",
int a = -1);
// Access member functions
const std::string &getName() const { return m_name; }
const std::string &getStreet() const { return m_street; }
const std::string &getTown() const { return m_town; }
const std::string &getState() const { return m_state; }
const int getAge() const { return m_age; }
// Mutator member functions
void setStreet(const std::string &street) { m_street = street; }
void setTown(const std::string &town) { m_town = town; }
void setState(const std::string &state) { m_state = state; }
void setAge(int age) { m_age = age; }
private:
std::string m_name;
std::string m_street;
std::string m_town;
std::string m_state;
int m_age;
};
#endif /* Person_h */You’d like to be able to say something like:
Person p("Fred Bloggs");
// ...
cout << p;
How can you accomplish this?
What’s required for the implementation of an overloaded operator << for class Person?
Look at the above expression:
operator <<is a binary operator- its left-hand operand is a type
ostream - its right-hand operand is a type
Person - you’d like to be able to string this expression together with others:
cout << "Person #" << id << " is: " << p
(which associates left to right.)
Questions:
- Can the resulting overloaded
operator <<function be a member function of the classPerson? - What should its argument list contain?
How many arguments? What types? Allconst? - What should its return type be?
Answers:
- It cannot be a member function of class
Person, because the left-hand operand is not of typePerson. - Since it can’t be a member function of class
Person, then it needs to be a regular function, but it still (typically) needs access to the internals of classPerson— it should therefore be afriendof classPerson. - Its argument list should reflect the left and right operands of the expression:
(ostream &os, const Person &p) - Since the associativity of the expression:
cout << "Person #" << id << " is: " << p;
is left-to-right, it is equivalent to:(((cout << "Person #") << id) << " is: ") << p;
which means that an expression like:cout << foo
must return a type reference to ostream(ostream &).
Example
So, we end up with a function that looks like:
ostream &operator<<(ostream &os, const Person &p);
So, in the class, it looks like:
//
// Person.h
// iostreams - User-defined types
//
// Created by Bryan Higgs on 9/4/24.
//
#ifndef Person_h
#define Person_h
#include <iostream>
class Person
{
public:
// Constructor
Person(const std::string &nm,
const std::string &street = "",
const std::string &town = "",
const std::string &state = "",
int a = -1);
// Access member functions
const std::string &getName() const { return m_name; }
const std::string &getStreet() const { return m_street; }
const std::string &getTown() const { return m_town; }
const std::string &getState() const { return m_state; }
const int getAge() const { return m_age; }
// Mutator member functions
void setStreet(const std::string &street) { m_street = street; }
void setTown(const std::string &town) { m_town = town; }
void setState(const std::string &state) { m_state = state; }
void setAge(int age) { m_age = age; }
friend
std::ostream &operator<<(std::ostream &os, const Person &p);
private:
std::string m_name;
std::string m_street;
std::string m_town;
std::string m_state;
int m_age;
};
#endif /* Person_h */Here’s the Person.cpp file:
//
// Person.cpp
// iostreams - User-defined types
//
// Created by Bryan Higgs on 9/4/24.
//
#include "Person.h"
#include <iostream>
// Constructor
Person::Person(const std::string &name,
const std::string &street,
const std::string &town,
const std::string &state,
int age)
: m_name(name), m_street(street), m_town(town),
m_state(state), m_age(age)
{ }
// Overloaded operator <<
std::ostream &operator<<(std::ostream &os, const Person &p)
{
return os << "Name: " << p.m_name << std::endl
<< "Street: " << p.m_street << std::endl
<< "Town: " << p.m_town << std::endl
<< "State: " << p.m_state << std::endl
<< "Age: " << p.m_age << std::endl;
}… and here’s a program to test this:
//
// main.cpp
// iostreams - User-defined types
//
// Created by Bryan Higgs on 9/4/24.
//
#include <iostream>
#include "Person.h"
int main(int argc, const char * argv[])
{
Person p("Fred Bloggs",
"15 Highland Terrace",
"Cleveland",
"OH",
34);
std::cout << p;
return 0;
}which outputs the following:
Name: Fred Bloggs
Street: 15 Highland Terrace
Town: Cleveland
State: OH
Age: 34
Program ended with exit code: 0