Concepts / Access Control with public and private

Access Control with public and private

Private members are accessible only through that interface.

  • Programming

Why Some Parts of a Type Stay Hidden

Imagine a collection of numbers kept in one place, with a set of operations that work on those numbers kept somewhere else. 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. This article uses a class to build that closer relationship and shows which parts of the class are visible to other code and which are hidden.

What do you think happens?

A program creates a Vector object v. Can the program name the internal storage directly, as v.elem, to read or change a value?

  • Yes, because every member of an object is reachable from outside it

  • No, because elem is private and can only be reached through the public interface

Reveal answer
Answer:

No. elem is a private member, so code outside the class cannot name it directly. The program reaches the stored values only through the public members the class provides.

Access control is what makes this boundary. Public members form the interface that everyone may use, and private members hold the hidden representation. Keep this answer in mind, because the rest of the article traces exactly how the public members reach the private ones.

Interface and Implementation in One Type

A class separates the interface of a type, which everyone may use, from its implementation, which holds the hidden data. The interface is the set of operations a user of the type is allowed to call. The implementation is the stored representation and the code that works on it. In a class, the public members define the interface, and the private members hold the implementation. Private members are accessible only through that interface.

The usual goal is to keep the stored representation out of reach of ordinary users. Hidden storage has three practical benefits. It makes the type easier to use, because users only learn the operations. It lets the author guarantee that the data stays consistent, because every change goes through operations the author controls. It also leaves room to improve the internal representation later without breaking code that depends only on the public operations.

Calling programsize()publicszprivate countoperator[]publicelemprivate storagemay callmay callreadsreaches into
Outside code can call the public members, but it has no direct path to the private members. The public members sit between the caller and the hidden storage.

Read the diagram from left to right. The calling program stands outside the class boundary. It can reach the two public members directly. Each public member then reaches the private members that it needs. The caller never has an edge pointing straight at elem or sz, which is exactly what the access rule enforces.

Parts of a Class Definition

A class can contain data members, function members, and type members. Data members hold the stored values. Function members are the operations that work on the data. Type members are named types declared inside the class. Each member is labeled with an access level, either public or private, and that label decides whether code outside the class can use it. The Vector example below uses data members and function members. It does not use a type member, but a type member is still a legitimate part of a class definition.

cpp
Notes on the code
  • The keyword public opens the section that outside code may use: the constructor, size, and the subscript operator.
  • The keyword private opens the section that holds the hidden representation: a pointer to storage and a count.
  • The constructor takes the number of elements, allocates storage for that many double values, and stores the pointer and the count in the private members.
  • size() reads the private count and returns it.
  • operator[] returns a reference into the private storage, so the caller can read or write one element without naming elem.
class Vectorclass namepublic:interface sectionsize(), operator[]function members, usableoutsideprivate:implementation sectionelem, szdata members, hidden
The class definition is divided by access labels. Everything after public is the interface, and everything after private is the hidden implementation.

Tracing a Call Through the Interface

Now follow a short sequence of uses from start to finish. The trace below uses the Vector class from the previous section. Keep track of which step touches the public interface and which step touches the private storage. The diagram shows the order of these interactions.

Calling programVector interfacePrivate members1. Vector(5)2. allocate 5 doubles, store elem and sz3. size()4. read sz5. v[2]6. return reference to elem[2]
The program's calls travel through the public interface, and each one reaches the private representation in order.
Worked example

Tracing a Vector Through Its Public Interface

A program creates a Vector with five elements, asks for its size, and then reads the element at index 2. Which private members change or get read at each step?

  1. 1
    Step 1: Create the Vector

    The program passes 5 to the constructor. The constructor allocates storage for five double values and stores the pointer in elem and the count 5 in sz. This is the first moment the private members receive values.

  2. 2
    Step 2: Call size()

    The program calls the public function size(). That function reads the private count sz and returns it. The caller receives 5 without ever naming sz.

  3. 3
    Step 3: Subscript with index 2

    The program uses the subscripting operator. The operator returns a reference into the private storage at position 2. The caller can read or write that element through the reference, but it still never names elem directly.

Result

The public members did all the work. Creating the object wrote to elem and sz, size() read sz, and the subscript operator gave access to one element of elem. The caller only ever used public names.

Which Names Outside Code Can Use

The access label settles every question about visibility. Public members can be used by any code that has a Vector. Private members can be used only by the members of the class itself. When you predict whether an outside line will compile, check the label on the member it names. The table summarizes the difference.

MemberAccess labelUsable from outside the class?Role in the design
Vector(int)publicYesCreates and sets up the object
size()publicYesReports the count stored in the private part
operator[]publicYesGives access to one element of the private storage
elemprivateNoHolds the pointer to the storage
szprivateNoHolds the number of elements

Public members form the interface, and private members are reached only through it.

Mistakes Beginners Make with Access Labels

  • Common mistake

    Assuming that every member of an object is visible to every caller

    What it looks like

    A caller writes v.elem to change one stored value.

    Why it fails

    elem sits after the private label, so the name is hidden from code outside the class.

    Do this instead

    Use the public subscript operator, which returns a reference into the private storage.

  • Common mistake

    Thinking private members are unusable everywhere

    What it looks like

    A beginner concludes that elem cannot be used at all and removes the constructor's access to it.

    Why it fails

    Private members are fully usable by the class's own functions. The constructor and size() both depend on them.

    Do this instead

    Keep the private members and let the class's own functions use them.

  • Common mistake

    Forgetting that a label controls every member below it until the next label

    What it looks like

    A programmer places a new function below private: and expects it to be public.

    Why it fails

    The label applies to all following members until another label appears.

    Do this instead

    Place each public operation above the private label, or add a new public: label before it.

Best practice

Keep the stored representation private so that the author can change it later without breaking code that uses only the public operations. Design the public part first, since it is the contract that users depend on.

Checking Your Understanding of Visibility

EASY

Given the Vector class above, which of these outside lines is allowed: (a) v.size(), (b) v.sz, or (c) v[0]? Explain your answer by naming the access label on each member.

Hints
  • Look at which names sit after public and which sit after private.
  • Think about whether a caller can reach a member through the interface instead of by name.
MEDIUM

Trace a new program that creates a Vector with three elements, reads its size, and then reads element 0. For each step, name the public member the caller uses and the private member that the public member touches.

Hints
  • The constructor is the first public member to run.
  • size() reads sz, and the subscript operator reaches elem.

Key Takeaways

  • A class separates the interface of a type, which everyone may use, from its implementation, which holds the hidden data.
  • Public members define the interface, and private members are accessible only through that interface.
  • Data members, function members, and type members all appear inside a class definition, each under an access label.
  • A public call such as size() or the subscript operator reaches private data inside the class, so outside code gets the benefit of the storage without naming it.
  • Hiding the representation makes a type easier to use, helps keep its data consistent, and allows the internal design to change later.

Related concepts

More concepts starting with A