The class
Access control is often referred to as
implementation hiding.
Including functions within structures (often referred to as
encapsulation[36])
produces a data type with characteristics and behaviors, but access control puts
boundaries within that data type, for two important reasons. The first is to
establish what the client programmers can and can’t use. You can build
your internal mechanisms into the structure without worrying that client
programmers will think that these mechanisms are part of the interface they
should be using.
This feeds directly into the second
reason, which is to separate the interface from the implementation.
If the
structure is used in a set of programs, but the client programmers can’t
do anything but send messages to the public interface, then you can
change anything that’s private without requiring modifications to
their code.
Encapsulation and access control, taken
together, invent something more than a C struct. We’re now in the
world of object-oriented programming, where a structure is describing a class of
objects as you would describe a class of fishes or a class of birds: Any object
belonging to this class will share these characteristics and behaviors.
That’s what the structure declaration has become, a description of the way
all objects of this type will look and act.
In the original OOP
language, Simula-67, the keyword
class was used to
describe a new data type. This apparently inspired Stroustrup to choose the same
keyword for C++, to emphasize that this was the focal point of the whole
language: the creation of new data types that are more than just C
structs with functions. This certainly seems like adequate justification
for a new keyword.
However, the use of class in C++
comes close to being an unnecessary keyword. It’s identical to the
struct keyword in absolutely every way except one: class defaults
to private, whereas struct defaults to public. Here are two
structures that produce the same result:
//: C05:Class.cpp
// Similarity of struct and class
struct A {
private:
int i, j, k;
public:
int f();
void g();
};
int A::f() {
return i + j + k;
}
void A::g() {
i = j = k = 0;
}
// Identical results are produced with:
class B {
int i, j, k;
public:
int f();
void g();
};
int B::f() {
return i + j + k;
}
void B::g() {
i = j = k = 0;
}
int main() {
A a;
B b;
a.f(); a.g();
b.f(); b.g();
} ///:~
The class is the fundamental OOP
concept in C++. It is one of the keywords that will not be set in bold in
this book – it becomes annoying with a word repeated as often as
“class.” The shift to classes is so important that I suspect
Stroustrup’s preference would have been to throw struct out
altogether, but the need for backwards compatibility with C wouldn’t allow
that.
Many people prefer a style of creating
classes that is more struct-like than class-like, because you override
the “default-to-private” behavior of the class by starting
out with public elements:
class X {
public:
void interface_function();
private:
void private_function();
int internal_representation;
};
The logic behind this is that it makes
more sense for the reader to see the members of interest first, then they can
ignore anything that says private. Indeed, the only reasons all the other
members must be declared in the class at all are so the compiler knows how big
the objects are and can allocate them properly, and so it can guarantee
consistency.
The examples in this book, however, will
put the private members first, like this:
class X {
void private_function();
int internal_representation;
public:
void interface_function();
};
Some people even go to the trouble of
decorating their own private names:
class Y {
public:
void f();
private:
int mX; // "Self-decorated" name
};
Because mX is already hidden in
the scope of Y, the m (for “member”) is unnecessary.
However, in projects with many global variables (something you should strive to
avoid, but which is sometimes inevitable in existing projects), it is helpful to
be able to distinguish inside a member function definition which data is global
and which is a
member.