ScAs: C14 Explained for Beginners

Let’s go through Chapter 14 (More skills for Object-Oriented Programming) like you’re a true beginner, with why you should / shouldn’t do things, and lots of concrete examples.


What Chapter 14 is trying to teach you

You already know classes/objects. This chapter gives you “extra powers” that make classes feel natural to use in real programs:

  1. Static members (stuff that belongs to the class, not each object)

  2. Friend functions (carefully allowing an outside function to access private data)

  3. Operator overloading (making +, ==, <<, etc. work on your objects)

  4. Modules (C++20) (a newer way to organize code instead of lots of headers)


1) Static members (class-wide data & functions)

The idea

Normally, every object gets its own copy of member variables.

class Dog {
public:
    int age;
};

If you make two dogs:

  • dog1.age and dog2.age are two separate ages.

But sometimes you want one shared piece of information for the whole class.

Example: “How many Dog objects exist?”

That’s what static data members are for.


1A) Static data member (shared variable)

Example: counting how many objects were made

class Dog {
private:
    static int count;   // shared by ALL Dog objects
public:
    Dog() { count++; }
    static int get_count() { return count; }
};

// Define it once (outside the class)
int Dog::count = 0;

Use it:

Dog a;
Dog b;
cout << Dog::get_count(); // prints 2

Why you should do this

Static is perfect when the data is about the class as a whole, like:

  • number of objects created

  • a shared tax rate

  • a shared “next ID” counter

Why you should NOT do this

Don’t use static for data that should be different per object:

  • price, name, age, gallons, etc.

If price were static, changing one product’s price changes all products, which is almost always wrong.


1B) Static member function (class-wide function)

Key beginner rule:

A static function does not belong to any one object.

So it cannot use normal (non-static) members like this->name because there is no “this object”.

Example:

class Dog {
public:
    string name;          // non-static

    static void print_name() {
        // cout << name;  // ERROR: which dog's name?
    }
};

What static functions are good for

  • helper/utility functions

  • counting objects

  • validation functions

  • input helper class (like a Console::get_int(...) idea)

Why an input helper is often static

Because you don’t need a Console object holding data like “consoleName” — you just want tools.

So this makes sense:

int x = Console::get_int("Enter age: ");

Creating a Console object like Console c; is pointless unless the object stores meaningful state.


2) Friend functions (letting one outside function access private stuff)

The problem friend functions solve

Normally, private means:

Only the class’s own member functions can access it.

Example:

class BankAccount {
private:
    double balance;
public:
    BankAccount(double b) : balance(b) {}
};

Outside the class:

BankAccount a(100);
cout << a.balance;  // not allowed (private)

That’s good! It prevents random code from messing with your object.


2A) What is a friend function?

A friend function is a function that is not a member of the class, but the class gives it permission to access private things.

class BankAccount {
private:
    double balance;
public:
    BankAccount(double b) : balance(b) {}

    friend void steal_money(BankAccount& acc);
};

void steal_money(BankAccount& acc) {
    acc.balance = 0; // allowed because it's a friend
}

Why you might use friend functions

Sometimes you have an operation that:

  • feels like it doesn’t “belong” to either object

  • needs access to private data of two different classes

  • commonly happens with stream operators like <<

Example idea: pour(tank, can) where both need private access.


2B) Why you usually should NOT use friend

Because it breaks encapsulation.

Friend is like saying:

“Okay, you can bypass my safety rules.”

That makes code harder to reason about and easier to break later.

Better alternative (often)

Use public methods that do the job safely:

class BankAccount {
private:
    double balance;
public:
    void withdraw(double amt) {
        if (amt <= balance) balance -= amt;
    }
    double get_balance() const { return balance; }
};

This keeps your rules inside the class.

Use friend when it genuinely makes design cleaner, not because it’s convenient.


3) Operator overloading (making operators work with objects)

The point

You want this to be possible:

Money m1(5.00);
Money m2(2.50);
Money m3 = m1 + m2;

But C++ doesn’t know what + means for your class unless you define it.

That’s operator overloading:

“When you see a + b and a and b are Money objects, call my code.”


3A) Overloading + and - (binary operators)

Correct beginner behavior

a + b should NOT change a or b.
It should create and return a new result.

class Money {
private:
    int cents;
public:
    Money(int c) : cents(c) {}

    Money operator+(const Money& other) const {
        return Money(this->cents + other.cents);
    }
};

Why const at the end?
Because adding money should not change the money objects.

Why const Money& other?

  • & avoids copying (faster)

  • const promises you won’t modify other

Why you should NOT mutate inputs in +

If + changed the left object, this becomes confusing:

Money a(100), b(50);
Money c = a + b;
// if a changed, that's surprising and dangerous

If you want mutation, use += instead.


3B) Overloading ++ (unary operator): prefix vs postfix

Prefix: ++x

  • changes x

  • returns the changed x

Postfix: x++

  • returns the old value

  • then changes x

That difference matters.

Prefix overload
Money& operator++() {
    cents++;
    return *this;  // returns the same object (by reference)
}
Postfix overload (has a dummy int)
Money operator++(int) {
    Money old = *this; // copy old value
    cents++;
    return old;        // return the copy (old)
}

Why postfix needs the “dummy int”

It’s just a marker so C++ can tell postfix and prefix apart.

Why prefix is usually preferred

Postfix often requires a copy (like old = *this) → extra work.


3C) Relational operators: ==, <, >

These should return bool.

Example:

bool operator==(const Money& other) const {
    return cents == other.cents;
}

Why you should do relational overloads

It lets your objects behave naturally:

if (m1 < m2) ...
if (m1 == m2) ...

Why you should NOT do weird meanings

Don’t make < mean something unexpected. Keep it intuitive, or future-you will suffer.


3D) Stream operators: << and >> (printing and reading)

You want:

cout << m1;
cin >> m1;

These are usually non-member functions because the left side is cout (an ostream), not your object.

Typical pattern:

class Money {
private:
    int cents;
public:
    Money(int c = 0) : cents(c) {}

    friend ostream& operator<<(ostream& out, const Money& m);
};

ostream& operator<<(ostream& out, const Money& m) {
    out << "$" << (m.cents / 100.0);
    return out; // IMPORTANT
}

Why you MUST return ostream&

So chaining works:

cout << "Money: " << m1 << "\n";

If you don’t return the stream, chaining breaks.


4) Modules (C++20) — modern file organization

The problem with old headers (.h)

Headers often cause:

  • huge compile times

  • accidental redefinitions / include-order issues

  • “leaking” implementation details

  • tons of #include clutter

Modules idea

Instead of:

#include "money.h"

You do:

import money;

And inside the module you control what is public using export.


4A) Exporting functions/classes

Example concept:

export module money;

export class Money {
    ...
};

Now other files can:

import money;
Money m;

Why modules are nice

  • cleaner boundaries (only exported things are public)

  • fewer include-guard problems

  • often faster builds (depends on compiler support)

Why you might NOT use modules yet

  • your course/compiler setup might not support modules well

  • build systems might be configured for traditional headers

So: learn them, but expect to still see headers in many classes/projects.


The “if you master only these, you’re solid” list

If you can confidently explain these, you’ve mastered the chapter:

  • Static data member = one shared variable for the whole class

  • Static function = no “this object”, so it can’t use non-static members

  • Friend function = outside function that can access private (use sparingly)

  • + should return a new object and not mutate operands

  • Prefix ++x vs postfix x++ and why postfix uses a dummy int

  • << returns ostream& to allow chaining

  • Modules use export and import to control visibility and organization