Class Members in C++
A class separates the interface of a type, which everyone may use, from its implementation, which holds the hidden data.
Why Hide the Stored Data
Suppose you keep a collection of numbers in one place and a set of operations that work on those numbers in another place. That arrangement is flexible, because any operation can touch the data in any way it likes. The flexibility has a cost. Nothing stops a caller from reaching into the storage and changing it in a way the author never intended.
A user-defined type that is meant to behave like a built-in type needs a closer relationship between how its data is stored and the operations offered for it. The usual goal is to keep the stored representation out of reach of ordinary users. This hidden storage makes the type easier to use, lets the author guarantee that the data stays consistent, and leaves room to improve the internal representation later without breaking code that depends only on the public operations. The C++ tool for this is the class.
What do you think happens?
Suppose a Vector object named v exists in a program. Can the calling code write v.elem = nullptr; directly to change the stored pointer?
Reveal answer
No. elem is a private member, so the compiler rejects the access from outside the class.
A class separates what callers may use (the public interface) from what is hidden (private members). Outside code can reach private data only through the public functions the class provides.
Interface and Implementation Inside One Class
A class separates the interface of a type, which everyone may use, from its implementation, which holds the hidden data. Inside a class definition, the members that appear after the keyword public form the interface. Callers can use those names. The members that appear after the keyword private are the implementation. Callers cannot name them directly, and they are reachable only through the public interface.
The diagram shows the structure the rest of this article depends on. The calling code has arrows only to the public functions. The private members appear at the far end, reached only when a public function does its work. If you remove the public functions, the private data has no route for outside code to reach it.
Data, Function, and Type Members
The members of a class can be data, function, or type members. Data members hold the values that make up an object's state. Function members perform operations on the class. Type members are members that are types rather than values or operations. Each of the three kinds can sit in either the public part or the private part of a class definition, so the access label and the kind of member are two separate questions to ask about any name.
| Member kind | What it is | Role in the Vector example |
|---|---|---|
| Data member | A value stored in each object | elem (the storage pointer) and sz (the count) |
| Function member | An operation the class provides | size() and operator[], plus the constructor |
| Type member | A member that is itself a type | None in the Vector example; the kind still exists in class definitions |
The three kinds of class members and where the Vector example uses them
The Vector Class as a Running Example
The running example for this article is a type called Vector that holds a sequence of double values. The class below is a simplified version written for this lesson. It shows the three things to notice: which lines appear after public, which appear after private, and what each public part lets a caller do. The private data pointer is named elem, and the element count is named sz in this sketch.
class Vector {
public:
Vector(int n)
: elem{new double[n]}, sz{n} { }
int size() const { return sz; }
double& operator[](int i) { return elem[i]; }
private:
double* elem;
int sz;
};- 1The public label opens the part of the class that callers may name.
- 2The constructor allocates storage with new double[n] and stores the pointer in elem and the count in sz.
- 3
size()returns sz, so a caller learns the element count without seeing sz itself. - 4operator[] returns a reference to one element of elem, so a caller can read or write that element.
- 5The private label hides elem and sz from every caller outside the class.
- public: begins the interface that callers may use
Vector(int n): the constructor takes the number of elements and allocates storage for that many double values- int
size()const: a public function member that reads the private count - double& operator[](int i): a public subscripting operation that returns a reference into the private storage
- private: begins the hidden implementation
- double* elem; int sz; are the private data members that hold the storage pointer and the count
Tracing a Call Through the Public Interface
Now follow a short sequence of uses from start to finish. First, the program creates a Vector and passes it the number of elements it wants. The constructor takes that number, allocates storage for that many double values, and stores both the pointer to the storage and the count in the private members. Second, the program calls size. That public function reads the private count and returns it. Third, the program uses subscripting to reach one element. The subscripting operator returns a reference into the private storage, so the caller can read or write that element without ever naming elem directly.
- Vector v(4) runs the constructor, which sets the private members
- v[2] uses the public subscript operator to reach private storage
v.size()returns the private count through a public function- The commented line would be rejected by the compiler
Where Each Member Lives in Memory
An object of the class holds the data members. In the Vector example, each object has its own storage pointer and its own count. Function members are not stored separately in each object. They belong to the class and are shared by all objects of that class. This is why the diagram below separates the members into those held per object and those shared at the class level.
Predicting Which Names an Outside Caller Can Use
Once you know the access labels, you can predict whether a given use from outside the class will succeed. The table applies that reasoning to the Vector example. Rely on the access label of the member itself, not on what the member is for.
| Use from outside the class | Member | Allowed? | Reason |
|---|---|---|---|
| Vector v(4); | constructor | Yes | The program creates the object through it, so it is part of the public interface |
| v.size() | size() | Yes | A public function member that reads the private count |
| v[2] = 7.5; | operator[] | Yes | A public subscripting operation that returns a reference into private storage |
| v.sz | sz | No | A private data member, reachable only through the public interface |
| v.elem | elem | No | A private data member, reachable only through the public interface |
Predicting external access from the public and private labels in the Vector class
Suppose a second class, Stack, places its data members under private and its operations under public. Without writing any code, predict whether calling code can use the data member named top directly, and whether it can call a public operation named push. Explain your answer using the public and private labels.
Hints
- Ask first whether the name sits after public or after private.
- A name that sits after private can be reached only through the public interface.
- A public operation that works on the private data is the intended route for callers.
Mistakes Beginners Make with Access Labels
Common mistake Treating private as meaning the data is unusable
What it looks likeAssuming that because sz is private, the Vector cannot use its own count at all
Why it failsPrivate members are hidden from outside code, but the class's public functions still use them to do their work, as
size()reads szDo this insteadRead private as hidden from callers, and check which public function reaches the data
Common mistake Assuming that every member must be public so callers can use the class
What it looks likeWriting every line of the Vector class after public
Why it failsThat exposes the stored representation, which the class exists to hide, so the author loses the ability to keep the data consistent
Do this insteadPlace the operations callers need after public and keep the stored representation after private
Common mistake Thinking each object carries its own copy of every function member
What it looks likeBelieving that two Vector objects each store a separate size() function
Why it failsData members are held per object, while function members belong to the class and are shared
Do this insteadExpect each object to hold its own data members, and the class to define the functions once
Common mistake Concluding that a class without a type member is incomplete
What it looks likeThinking the Vector class is missing something because it has no nested type
Why it failsType members are one kind of class member that a class may use, but the Vector example does not need one
Do this insteadRecognize the three kinds of members, and accept that a particular class may use only some of them
Key Takeaways
- A class separates the interface of a type, which everyone may use, from its implementation, which holds the hidden data.
- Members of a class can be data, function, or type members, and the public or private label controls who may use each one.
- Public members define the interface, and private members are reachable only through that interface, which keeps the representation consistent and free to change later.
- When a program calls a public function, control passes from the caller through the public interface to the private data and back, as in
size()reading the private count. - Data members are held in each object, while function members are shared by the class.