At this point, we have defined an interface for our class; but nothing forces users to use that interface. Our class is not yet encapsulated—users can reach inside a Sales_data
object and meddle with its implementation. In C++ we use access specifiers to enforce encapsulation:
• Members defined after a public
specifier are accessible to all parts of the program. The public
members define the interface to the class.
• Members defined after a private
specifier are accessible to the member functions of the class but are not accessible to code that uses the class. The private
sections encapsulate (i.e., hide) the implementation.
Redefining Sales_data
once again, we now have
class Sales_data {
public: // access specifier added
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: // access specifier added
double avg_price() const
{ return units_sold ? revenue/units_sold : 0; }
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
The constructors and member functions that are part of the interface (e.g., isbn
and combine
) follow the public
specifier; the data members and the functions that are part of the implementation follow the private
specifier.
A class may contain zero or more access specifiers, and there are no restrictions on how often an access specifier may appear. Each access specifier specifies the access level of the succeeding members. The specified access level remains in effect until the next access specifier or the end of the class body.