Classes & Objects
Everything so far has been loose variables and standalone functions. A class bundles related data and the functions that operate on it into a single unit — a blueprint. An object is one specific instance built from that blueprint, with its own independent copy of the data.
Defining a class
A bank account has data (an owner, a balance) and behavior (depositing changes the balance). A class groups exactly that. By default, class members are private, so this example marks them public explicitly to keep things simple for now:
#include <iostream> #include <string> class BankAccount { public: std::string owner; double balance; void deposit(double amount) { balance += amount; std::cout << "Deposited " << amount << ". New balance: " << balance << std::endl; } }; int main() { BankAccount account; account.owner = "Jamie"; account.balance = 100; account.deposit(50); return 0; }
Deposited 50. New balance: 150
owner and balance are the account's data members. deposit is a member function — inside it, balance refers to that particular object's balance with no extra qualification needed.
One blueprint, many objects
Each BankAccount variable you declare is a completely independent object. Changing one never touches another, even though they share the same class:
#include <iostream> #include <string> class BankAccount { public: std::string owner; double balance; void deposit(double amount) { balance += amount; } }; int main() { BankAccount account1; account1.owner = "Jamie"; account1.balance = 100; BankAccount account2; account2.owner = "Sasha"; account2.balance = 500; account1.deposit(25); std::cout << account1.owner << ": " << account1.balance << std::endl; std::cout << account2.owner << ": " << account2.balance << std::endl; return 0; }
Jamie: 125 Sasha: 500
Depositing into account1 left account2's balance of 500 completely undisturbed — separate objects, separate memory, even though they came from the same class definition.
private members and methods that return a result
Marking data private means it can only be touched through the class's own member functions, not poked directly from outside — the encapsulation that makes classes worth using in the first place:
#include <iostream> class BankAccount { private: double balance; public: void setBalance(double amount) { balance = amount; } bool withdraw(double amount) { if (amount > balance) { std::cout << "Insufficient funds." << std::endl; return false; } balance -= amount; return true; } double getBalance() { return balance; } }; int main() { BankAccount account; account.setBalance(100); std::cout << std::boolalpha << account.withdraw(30) << std::endl; std::cout << account.getBalance() << std::endl; std::cout << std::boolalpha << account.withdraw(200) << std::endl; return 0; }
true 70 Insufficient funds. false
Because balance is private, code outside the class can't write account.balance = -9999; to bypass withdraw — the only way in or out is through the public member functions, which enforce the insufficient-funds rule every time.
setBalance to initialize an object, as this example does, works but is clunky — it's easy to forget to call it, leaving an object with a garbage, uninitialized balance. The next lesson, on constructors, fixes exactly that by letting you set required data up front, at the moment an object is created.