Concepts / Class Members in C++

Class Members in C++

A class separates the interface of a type, which everyone may use, from its implementation, which holds the hidden data.

  • Programming

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?

  • Yes, because every member of a class is visible to callers

  • No, because elem is a private member and only the public interface can reach it

Reveal answer
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.

main()calling codesize()public functionszprivate countoperator[]public functionelemprivate storage pointercallscallsreadsindexes
Which parts of a class can outside code touch directly, and which parts sit behind the public interface? Calling code reaches the public functions, and those functions reach the private data. The calling code has no direct arrow to the private members.

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 kindWhat it isRole in the Vector example
Data memberA value stored in each objectelem (the storage pointer) and sz (the count)
Function memberAn operation the class providessize() and operator[], plus the constructor
Type memberA member that is itself a typeNone 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.

Code walkthrough
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;
};
  1. 1
    The public label opens the part of the class that callers may name.
  2. 2
    The constructor allocates storage with new double[n] and stores the pointer in elem and the count in sz.
  3. 3
    size() returns sz, so a caller learns the element count without seeing sz itself.
  4. 4
    operator[] returns a reference to one element of elem, so a caller can read or write that element.
  5. 5
    The 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.

main()calling programVector interfacepublic membersprivate dataelem and szVector(4)allocate, store elem and szsize()read sz4returns 4
When the program calls a public member function, control passes from the caller through the public interface to the private data and then back. Read the messages from top to bottom in the order listed.
cpp
Notes on the code
  • 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.

elempointer to double storagesize()shared by the classsznumber of elementsoperator[]shared by the class
What does one Vector object hold, and what is shared among all Vector objects? Each object holds its own data members. The function members are defined once for the class.

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 classMemberAllowed?Reason
Vector v(4);constructorYesThe program creates the object through it, so it is part of the public interface
v.size()size()YesA public function member that reads the private count
v[2] = 7.5;operator[]YesA public subscripting operation that returns a reference into private storage
v.szszNoA private data member, reachable only through the public interface
v.elemelemNoA private data member, reachable only through the public interface

Predicting external access from the public and private labels in the Vector class

EASY

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 like

    Assuming that because sz is private, the Vector cannot use its own count at all

    Why it fails

    Private members are hidden from outside code, but the class's public functions still use them to do their work, as size() reads sz

    Do this instead

    Read 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 like

    Writing every line of the Vector class after public

    Why it fails

    That exposes the stored representation, which the class exists to hide, so the author loses the ability to keep the data consistent

    Do this instead

    Place 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 like

    Believing that two Vector objects each store a separate size() function

    Why it fails

    Data members are held per object, while function members belong to the class and are shared

    Do this instead

    Expect 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 like

    Thinking the Vector class is missing something because it has no nested type

    Why it fails

    Type members are one kind of class member that a class may use, but the Vector example does not need one

    Do this instead

    Recognize 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.

Related concepts

More concepts starting with C