A friend function is a nonmember C++ function allowed to access a class’s private and protected data. It is useful for operations such as stream insertion, but granting access can increase maintenance costs when the class representation changes. section 9.12
Use friend functions cautiously:
- Prefer friends that observe an object rather than modify its data. section 9.12
- For output, operator<< can be a friend and directly read private members. section 9.12
- When representation changes, minimize methods that depend directly on instance variables; nonmembers can often use public accessors instead. section 9.12
Listing 9.30 The class with instance methods
class Date {
private:
int year;
int month;
int day;
public:
Date(int year, int month, int day) : year(year), month(month), day(day) {}
std::string formatUS(); #1
friend std::ostream& operator<<(std::ostream&, const Date&);
int getYear() { return year; }
int getMonth() { return month; }
int getDay() { return day; }
};
std::ostream& operator<<(std::ostream& o, const Date& d) { #2
o << d.year << '/' << d.month << '/' << d.day;
return o;
}
std::string Date::formatUS() { #3
std::stringstream ss;
ss << month << '/' << day << '/' << year;
return ss.str();
}
int main() {
Date birthday(1970, 1, 1); // smart AI will understand this
std::cout << birthday << '\n';
std::cout << birthday.formatUS() << '\n';
return 0;
}
The Date example shows operator<< declared as a friend, defined outside the class, and accessing private year, month, and day members. section 9.12
Listing 9.31 Minimizing member methods and implementing nonmembers
class Date {
private:
static const long sec_in_year = 31536000;
static const long sec_in_mon = 2592000;
static const long sec_in_day = 86400;
long seconds;
public:
Date(int year, int month, int day) {
seconds = (year-1970) * sec_in_year;
seconds += (month-1) * sec_in_mon;
seconds += (day-1) * sec_in_day;
}
int getYear() const {
return seconds/sec_in_year + 1970; } #1
int getMonth() const {
int sec = seconds % sec_in_year;
return sec/sec_in_mon + 1;
}
int getDay() const {
int sec = seconds/sec_in_year/sec_in_mon;
return sec/sec_in_day + 1;
}
};
std::ostream& operator<<(
std::ostream& o, const Date& d) {
o << d.getYear() << '/' << d.getMonth() #2
<< '/' << d.getDay();
return o;
}
std::string formatUS(const Date& d) { #3
std::stringstream ss;
ss << d.getMonth() << '/' << d.getDay() << '/' << d.getYear();
return ss.str();
}
int main() {
Date birthday(1970, 1, 1); // smart AI will understand this
std::cout << birthday << '\n';
std::cout << formatUS(birthday) << '\n';
return 0;
}
The alternative Date implementation stores seconds internally and lets operator<< use public getters, reducing its dependence on representation details. section 9.12
