Clases, Structs y Encapsulacion
Comprende la diferencia entre struct y class, los especificadores de acceso, la encapsulacion, las funciones miembro const, los miembros estaticos y la inicializacion de agregados moderna.
Que Es una Clase?
Una clase en C++ es un tipo definido por el usuario que agrupa datos (variables miembro) y operaciones sobre esos datos (funciones miembro) en una sola unidad. Este agrupamiento se llama encapsulacion — uno de los cuatro pilares de la POO.
La idea clave es que una clase define un invariante: un conjunto de reglas que siempre deben ser verdaderas sobre su estado interno. Por ejemplo, una clase Date podria requerir que el mes este siempre entre 1 y 12. La encapsulacion te permite imponer estos invariantes controlando el acceso al estado interno.
struct vs class
En C++, struct y class son casi identicos. La unica diferencia es el especificador de acceso por defecto: struct por defecto es public, mientras que class por defecto es private. Convencion: usa struct para agregados de datos simples, class cuando tengas invariantes que mantener.
#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;
}Especificadores de Acceso y el Puntero this
C++ proporciona tres niveles de acceso: public (cualquiera puede acceder), private (solo la clase y sus amigos), y protected (la clase, sus amigos y las clases derivadas). El puntero this es un puntero implicito al objeto actual disponible dentro de cada funcion miembro no estatica.
#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;
}Miembros Estaticos y friend
Los miembros estaticos pertenecen a la clase, no a ninguna instancia. Usalos para estado compartido o funciones utilitarias. La palabra clave friend otorga a una funcion no miembro o a otra clase acceso a los miembros privados — usala con moderacion, ya que rompe la encapsulacion.
#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;
}Inicializacion de Agregados e Inicializadores Designados (C++20)
Un agregado es una clase o struct sin constructores declarados por el usuario, sin miembros de datos no estaticos privados/protegidos, sin funciones virtuales y sin clases base virtuales. C++20 agrega los inicializadores designados, que te permiten nombrar que miembros estas inicializando.
#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;
}Haz los miembros de datos private por defecto y expone solo lo necesario a traves de una interfaz publica minima. Si una clase no tiene invariante que mantener (es solo un paquete de datos), usa un struct con miembros publicos en su lugar. No agregues getters y setters para cada campo — eso anula el proposito de la encapsulacion. En su lugar, proporciona operaciones significativas como deposit() y withdraw() en lugar de set_balance().
Si una funcion miembro no modifica el estado del objeto, marcala como const. Sin const, no puedes llamar a la funcion sobre un objeto const o a traves de una referencia const — lo cual sucede todo el tiempo al pasar objetos por const&. Un const faltante en un accesor es una fuente comun de errores de compilacion en bases de codigo mas grandes.
structyclassdifieren solo en el acceso por defecto:structes public,classes private- La encapsulacion protege los invariantes de la clase — usa datos
privatecon una interfaz publica significativa - Marca las funciones miembro que no mutan como
constpara que funcionen con referenciasconst - Usa
inline static(C++17) para miembros estaticos definidos en headers - Los inicializadores designados de C++20 hacen la inicializacion de agregados legible y autodocumentada
frienddebe usarse con moderacion — tipicamente para sobrecargas de operadores que necesitan acceso privado
Quiz — Pon a prueba tus conocimientos
(15 XP)1. Cual es la UNICA diferencia entre `struct` y `class` en C++?
2. Por que deberias marcar las funciones miembro accesoras como `const`?
3. Que logra `inline static int count_ = 0;` en C++17?