Dataslope logoDataslope

Classes and Objects

Bundling data with the operations that act on it — the foundation of object-oriented programming.

A struct that holds data is useful. But often the data only makes sense together with certain operations. A BankAccount is its balance plus the rules for deposits and withdrawals. A Timer is its start time plus the way you start, stop, and read it. Putting data and behaviour together is the central idea of object-oriented programming (OOP), and in C++ it's done with classes.

From struct to class

Here is a struct holding bank account data:

struct BankAccount {
    double balance;
};

The trouble: any code anywhere can do account.balance = -1e9; — nothing protects the rule that balances should not go negative.

A class lets us say: "the balance belongs to the account; outside code may only ask it to deposit or withdraw."

Code Block
C++ 20 (202002L)

What changed:

  • Access modifiers public: and private: split the class into "the part the outside world sees" and "the part only methods may touch."
  • The data field is now named balance_ (trailing underscore is a common convention to mark members).
  • A constructor sets the field at creation time. We'll devote a whole chapter to constructors next.
  • Methods (also called member functions) operate on the object. const after the parameter list means "I promise not to modify the object."

Anatomy of a class

The public methods form the interface. The private fields are the implementation. The interface promises that the balance is always sensible; the implementation guards that promise.

Objects vs. classes

A class is a type — a description. An object is a value of that type — a specific instance.

BankAccount a(100.0);    // a is an object
BankAccount b(500.0);    // b is another, independent object

a.deposit(50.0);         // affects only a

Each object has its own copy of the private fields. Methods, on the other hand, exist once per class and operate on whichever object they were called on.

The hidden this pointer

When you call a.deposit(50.0), the compiler passes a's address as a hidden first argument. Inside the method, the implicit pointer this refers to the object:

void deposit(double amount) {
    if (amount > 0) this->balance_ += amount;   // same as balance_ += amount
}

You rarely write this-> explicitly, but it's there. It's how the method knows which account's balance to change.

Constness on methods

Methods marked const may be called on const objects and may not modify the object. Methods without const may modify it.

class Timer {
public:
    void start();                  // modifies — non-const
    double seconds() const;        // observes — const
};

Mark every method that doesn't change state as const. This is one of the most useful disciplines in C++.

A small but complete example

Code Block
C++ 20 (202002L)

The class hides its fields, exposes a small clear interface, and keeps an obvious invariant ("count_ only ever goes up by one").

Challenge

Challenge
C++ 20 (202002L)
A Counter class

Complete the Counter class so that it supports tick() (increments by 1), reset() (sets back to 0), and value() const (returns the current count). The main function is wired to call them and print 2 0 if your class works correctly.

Test your understanding

QuestionSelect one

What is the difference between a class and an object?

They are the same thing.

A class is a type — a blueprint describing data and operations. An object is an instance of that type, with its own copy of the data.

An object is a class with no methods.

A class can only exist once per program.

QuestionSelect one

Why mark a method const?

It makes the method run faster.

It is required by the compiler.

It documents and enforces that the method does not modify the object, allowing it to be called on const instances and helping readers reason about behaviour.

It hides the method from outside code.

Next: why we hide fields — encapsulation and the idea of an invariant.

On this page