Now that the data members of Sales_data
are private
, our read, print
, and add
functions will no longer compile. The problem is that although these functions are part of the Sales_data
interface, they are not members of the class.
A class can allow another class or function to access its nonpublic
members by making that class or function a friend. A class makes a function its friend by including a declaration for that function preceded by the keyword friend
:
class Sales_data {
// friend declarations for nonmember Sales_data operations added
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream &read(std::istream&, Sales_data&);
friend std::ostream &print(std::ostream&, const Sales_data&);
// other members and access specifiers as before
public:
Sales_data() = default;
Sales_data(const std::string &s, unsigned n, double p):
bookNo(s), units_sold(n), revenue(p*n) { }
Sales_data(const std::string &s): bookNo(s) { }
Sales_data(std::istream&);
std::string isbn() const { return bookNo; }
Sales_data &combine(const Sales_data&);
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
// declarations for nonmember parts of the Sales_data interface
Sales_data add(const Sales_data&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
Friend declarations may appear only inside a class definition; they may appear anywhere in the class. Friends are not members of the class and are not affected by the access control of the section in which they are declared. We’ll have more to say about friendship in § 7.3.4 (p. 279).
Ordinarily it is a good idea to group friend declarations together at the beginning or end of the class definition.
A friend declaration only specifies access. It is not a general declaration of the function. If we want users of the class to be able to call a friend function, then we must also declare the function separately from the friend declaration.
To make a friend visible to users of the class, we usually declare each friend (outside the class) in the same header as the class itself. Thus, our Sales_data
header should provide separate declarations (aside from the friend declarations inside the class body) for read
, print
, and add
.
Many compilers do not enforce the rule that friend functions must be declared outside the class before they can be used.
Some compilers allow calls to a friend
function when there is no ordinary declaration for that function. Even if your compiler allows such calls, it is a good idea to provide separate declarations for friend
s. That way you won’t have to change your code if you use a compiler that enforces this rule.
Exercise 7.20: When are friends useful? Discuss the pros and cons of using friends.
Exercise 7.21: Update your Sales_data
class to hide its implementation. The programs you’ve written to use Sales_data
operations should still continue to work. Recompile those programs with your new class definition to verify that they still work.
Exercise 7.22: Update your Person
class to hide its implementation.