C++ Tutorial
C++ Encapsulation
Keep fields private. Expose getters and setters so the class can protect its own rules.
Hide the data, publish the rules
Encapsulation means the object owns its fields. Callers do not poke at them. They call methods. The class then decides what is allowed: a score cannot go below zero, a name cannot be empty, a tank cannot hold more than its capacity.
In C++ you do this with private data and public getters and setters. The previous chapter introduced access specifiers. This one is why they exist.
Private fields
Put data under private:. Code outside the class cannot read or write those names. Methods of the same class still can. That is the whole point: the class is the only place that touches the raw values.
Example
#include <iostream>
#include <string>
using namespace std;
class Player {
private:
string name;
int score;
public:
Player(string n, int s) {
name = n;
score = s;
}
string getName() {
return name;
}
int getScore() {
return score;
}
};
int main() {
Player p("Ada", 12);
cout << p.getName() << " has " << p.getScore() << endl;
return 0;
}p.score = -4; would not compile. The field is private. You must go through a method if you want to change it.
Getters
A getter returns a copy (or a const reference) of a field. Name them getX in this tutorial so the intent is obvious. They let the rest of the program read state without knowing how it is stored.
Later you can change score from an int to something else and keep the same getter. Callers do not notice.
Setters that protect invariants
A setter writes a field only when the new value is valid. If it is not, leave the old value or refuse the change. That is the rule living in one place instead of in every call site.
Example
#include <iostream>
using namespace std;
class Score {
private:
int value;
public:
Score() {
value = 0;
}
int get() {
return value;
}
void add(int points) {
if (points < 0) {
return;
}
value += points;
}
};
int main() {
Score s;
s.add(10);
s.add(-3);
cout << s.get() << endl;
return 0;
}The second add is ignored. The score stays 10. If value were public, a caller could set it to anything.
Public data skips the class
| Public field | Private field + methods | |
|---|---|---|
| Who writes it | Anyone with the object | Only the class |
| Checks | You hope every caller remembers | One setter, one rule |
| Rename later | Every use site breaks | Only the class body changes |
Structs with all-public fields are fine for bags of data with no rules. The moment a value has a legal range, make it a class and hide the field.
A tank that cannot overflow
Capacity is set in the constructor. fill never stores more than that. getLiters is the only way to read the current amount.
Example
#include <iostream>
using namespace std;
class Tank {
private:
int liters;
int cap;
public:
Tank(int capacity) {
cap = capacity;
liters = 0;
}
int getLiters() {
return liters;
}
void fill(int amount) {
if (amount < 0) {
return;
}
liters += amount;
if (liters > cap) {
liters = cap;
}
}
};
int main() {
Tank t(50);
t.fill(40);
t.fill(20);
cout << t.getLiters() << endl;
return 0;
}Output is 50, not 60. The tank enforced its own limit.
What to keep private
- Anything that must stay consistent with something else (balance and transactions, size and buffer).
- Anything with a range or format (age, email, percentage).
- Helpers that are not part of the public story of the type.
Getters and setters are not decoration. If a setter only assigns with no check, you still gained the option to add a check later without hunting through the program. Next: a class that reuses another class — inheritance.
Worked examples
The short listings above are there so you can see the grammar. The programs here use the same statements on quantities that already have units: a speed, a pH, a count of bases. They are classroom numbers. Air resistance is ignored. g is 9.81 m/s² unless a line says otherwise.
Open them in the C++ editor at /cpp/try. Change one measurement and check whether the result still has the right unit.
Chemistry
pH that rejects nonsense
pH on the usual aqueous scale sits between about 0 and 14. set() refuses −1. The field is private so callers cannot assign it directly and skip the check.
pH = −log₁₀[H⁺]
Concentrated acids can have negative pH. The 0–14 gate is a teaching clamp, not a law.
Example
#include <iostream>
using namespace std;
class Ph {
double value;
public:
Ph() : value(7.0) {}
bool set(double p) {
if (p < 0.0 || p > 14.0) return false;
value = p;
return true;
}
double get() { return value; }
};
int main() {
Ph reading;
cout << boolalpha << reading.set(4.2) << " " << reading.get() << endl;
cout << reading.set(-1.0) << " " << reading.get() << endl;
return 0;
}Physics
Speed that cannot go negative
Speed is a magnitude. A setter that rejects v < 0 is the class protecting its own rule. 15 m/s is stored. −3 is ignored, so the object still holds 0 from construction.
Example
#include <iostream>
using namespace std;
class Speed {
double v;
public:
Speed() : v(0.0) {}
void set(double value) {
if (value >= 0.0) v = value;
}
double get() { return v; }
};
int main() {
Speed s;
s.set(15.0);
cout << s.get() << endl;
s.set(-3.0);
cout << s.get() << endl;
return 0;
}