Classes, Structs & Encapsulation
Understand the difference between struct and class, access specifiers, encapsulation, const member functions, static members, and modern aggregate initialization.
What Is a Class?
A class in C++ is a user-defined type that bundles data (member variables) and operations on that data (member functions) into a single unit. This bundling is called encapsulation — one of the four pillars of OOP.
The key insight is that a class defines an invariant: a set of rules that must always be true about its internal state. For example, aDateclass might require that the month is always between 1 and 12. Encapsulation lets you enforce these invariants by controlling access to internal state.
struct vs class
In C++,structandclassare almost identical. The only difference is the default access specifier:structdefaults topublic, whileclassdefaults toprivate. Convention: usestructfor plain data aggregates,classwhen you have invariants to maintain.
#include <iostream>
#include <string>
// struct: members are public by default
struct Point {
double x; // public
double y; // public
};
// class: members are private by default
class BankAccount {
std::string owner; // private
double balance; // private
public:
BankAccount(const std::string& name, double initial)
: owner{name}, balance{initial} {}
double get_balance() const { return balance; }
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
};
int main() {
Point p{3.0, 4.0}; // aggregate initialization
std::cout << p.x << ", " << p.y << '\n';
BankAccount acct{"Alice", 1000.0};
acct.deposit(500.0);
std::cout << acct.get_balance() << '\n'; // 1500
// acct.balance = -999; // ERROR: private member
return 0;
}Access Specifiers & the this Pointer
C++ provides three access levels: public (anyone can access), private (only the class and its friends), and protected (the class, its friends, and derived classes). Thethispointer is an implicit pointer to the current object available inside every non-static member function.
#include <iostream>
#include <string>
class Employee {
private:
std::string name_;
int id_;
protected:
double salary_; // accessible to derived classes
public:
Employee(const std::string& name, int id, double salary)
: name_{name}, id_{id}, salary_{salary} {}
// const member function: promises not to modify the object
const std::string& get_name() const { return name_; }
int get_id() const { return id_; }
// 'this' pointer usage — return *this for method chaining
Employee& set_name(const std::string& name) {
this->name_ = name; // 'this->' disambiguates
return *this;
}
Employee& set_id(int id) {
this->id_ = id;
return *this;
}
void print() const {
std::cout << "Employee{" << name_ << ", #" << id_
<< ", $" << salary_ << "}\n";
}
};
int main() {
Employee e{"Bob", 42, 75000.0};
e.set_name("Robert").set_id(43); // method chaining via *this
e.print(); // Employee{Robert, #43, $75000}
return 0;
}Static Members & friend
Static members belong to the class, not to any instance. Use them for shared state or utility functions. The friend keyword grants a non-member function or another class access to private members — use sparingly, as it breaks encapsulation.
#include <iostream>
class Widget {
int value_;
// C++17: inline static — define in header, no .cpp needed
inline static int count_ = 0;
public:
explicit Widget(int v) : value_{v} { ++count_; }
~Widget() { --count_; }
static int get_count() { return count_; }
// friend function can access private members
friend std::ostream& operator<<(std::ostream& os, const Widget& w);
};
std::ostream& operator<<(std::ostream& os, const Widget& w) {
return os << "Widget(" << w.value_ << ')'; // accesses private value_
}
int main() {
Widget a{10}, b{20};
std::cout << Widget::get_count() << '\n'; // 2
std::cout << a << '\n'; // Widget(10)
{
Widget c{30};
std::cout << Widget::get_count() << '\n'; // 3
}
std::cout << Widget::get_count() << '\n'; // 2
return 0;
}Aggregate Initialization & Designated Initializers (C++20)
An aggregate is a class or struct with no user-declared constructors, no private/protected non-static data members, no virtual functions, and no virtual base classes. C++20 adds designated initializers, letting you name which members you are initializing.
#include <iostream>
#include <string>
struct Config {
std::string host = "localhost";
int port = 8080;
bool use_tls = false;
int max_connections = 100;
};
int main() {
// Traditional aggregate initialization
Config c1{"example.com", 443, true, 200};
// C++20 designated initializers — much more readable
Config c2{
.host = "api.example.com",
.port = 443,
.use_tls = true
// .max_connections uses default value 100
};
std::cout << c2.host << ':' << c2.port << '\n';
std::cout << "TLS: " << std::boolalpha << c2.use_tls << '\n';
std::cout << "Max: " << c2.max_connections << '\n'; // 100
return 0;
}Make data members private by default and expose only what is needed through a minimal public interface. If a class has no invariant to maintain (it's just a bundle of data), use a struct with public members instead. Do not add getters and setters for every field — that defeats the purpose of encapsulation. Instead, provide meaningful operations likedeposit()andwithdraw()rather thanset_balance().
If a member function does not modify the object's state, mark itconst. Withoutconst, you cannot call the function on aconstobject or through aconstreference — which happens all the time when passing objects byconst&. A missingconston an accessor is a common source of compilation errors in larger codebases.
structandclassdiffer only in default access:structis public,classis private- Encapsulation protects class invariants — use
privatedata with a meaningful public interface - Marcați funcțiile membre care nu sunt mutante
const, astfel încât să funcționeze cu referințeconst - Utilizați
inline static(C++17) pentru membrii statici definiți în anteturi - Inițializatoarele desemnate C++20 fac inițializarea agregată lizibilă și auto-documentată
friendar trebui utilizat cu moderație - de obicei pentru supraîncărcările operatorului care necesită acces privat
Test — Verifică-ți cunoștințele
(15 XP)1. Care este SINGURA diferență dintre`struct`și`class`în C++?
2. De ce ar trebui să marcați funcțiile membre accesorii ca`const`?
3. Ce realizează`inline static int count_ = 0;`în C++17?